๐Ÿš€ ็ง’้€Ÿใงๅญฆใถ SAP AI Core Agent ้–‹็™บ ๐Ÿš€ – Part 7 SAPUI5 ใงใƒใƒฃใƒƒใƒˆUI ใ‚’ๆง‹็ฏ‰
Share

[[{“value”:”

็”Ÿๆˆ AI ใฏ็ ”็ฉถๅฎคใฎๅค–ใธ้ฃ›ใณๅ‡บใ—ใ€ไปŠใ‚„ใƒ“ใ‚ธใƒใ‚น็พๅ ดใฎๅธธ่ญ˜ใ‚’ๅก—ใ‚Šๆ›ฟใˆใฆใ„ใพใ™ใ€‚SAPใฏๅ…จ้€ŸๅŠ›ใงใใฎๆณขใซไน—ใฃใฆใ„ใพใ™ใ€‚ใ“ใฎใƒ–ใƒญใ‚ฐใ‚ทใƒชใƒผใ‚บใงใฏใ€SAPย AIย Core ใฎๆ—ขๅฎšใƒขใƒ‡ใƒซใ‚’ๆœ€้€Ÿใงๅ‘ผใณๅ‡บใ—ใ€ๅฎŸๅ‹™ใงไฝฟใˆใ‚‹ AI ใ‚จใƒผใ‚ธใ‚งใƒณใƒˆใธๆ‹กๅผตใ™ใ‚‹โ€œ็ง’้€Ÿใƒใƒณใ‚บใ‚ชใƒณโ€ใ‚’ใŠๅฑŠใ‘ใ—ใพใ™ใ€‚

ใŠ็Ÿฅใ‚‰ใ›
You can find the English version here.

ย 

๐Ÿ“–ย ๆœฌใ‚ทใƒชใƒผใ‚บใงๅญฆในใ‚‹ใ“ใจ

  • SAPย AIย Core ไธŠใงใ‚ซใ‚นใ‚ฟใƒ  AI Agent ใ‚’ โ€œ็ง’้€Ÿโ€ ใงๅ‹•ใ‹ใ™ๆ–นๆณ•
  • LangChainใƒปGoogle ๆคœ็ดขใƒ„ใƒผใƒซใƒปRAGใ‚’ไฝฟใฃใŸๅฎŸ่ฃ…
  • AI Agent ใ‚’ RESTย API ๅŒ–ใ—ใ€SAPUI5/Fiori ใฎ UI ใซ่ผ‰ใ›ใ€Cloud Foundryใซใƒ‡ใƒ—ใƒญใ‚คใ™ใ‚‹ๆ‰‹้ †

ๅญฆ็ฟ’ๆ™‚้–“
ๅ„Part ใฏย 10โ€“15 ๅˆ†ย ใง่ชญใ‚ใ‚‹๏ผ†ๆ‰‹ใ‚’ๅ‹•ใ‹ใ›ใ‚‹ใƒœใƒชใƒฅใƒผใƒ ใ‚’ไบˆๅฎšใ—ใฆใ„ใพใ™ใ€‚

ย 

๐Ÿ—บ๏ธ ้€ฃ่ผ‰ใƒญใƒผใƒ‰ใƒžใƒƒใƒ—

ใ“ใฎ่จ˜ไบ‹ใŒใŠๅฝนใซ็ซ‹ใฃใŸใ‚‰ใ€ใœใฒโ€ฏKudosโ€ฏใ‚’ๆŠผใ—ใฆใ„ใŸใ ใ‘ใ‚‹ใจๅŠฑใฟใซใชใ‚Šใพใ™ใ€‚ ใ€Œใ“ใ“ใ‚’ใ‚‚ใฃใจ่ฉณใ—ใ็Ÿฅใ‚ŠใŸใ„ใ€ใ€Œๅˆฅใฎใƒ†ใƒผใƒžใ‚‚ๅ–ใ‚ŠไธŠใ’ใฆใปใ—ใ„ใ€ใชใฉใ€ใ”่ฆๆœ›ใŒใ‚ใ‚ŒใฐใŠๆฐ—่ปฝใซใ‚ณใƒกใƒณใƒˆใใ ใ•ใ„๏ผ


SAPUI5 ใงใƒใƒฃใƒƒใƒˆUI ใ‚’ๆง‹็ฏ‰

1 | ใฏใ˜ใ‚ใซ

ไปŠๅ›žใฎPart ใงใฏใ€ๅ‰ๅ›žใพใงใซๆง‹็ฏ‰ใ—ใŸ AI Agent ใ‚’ใ€SAP UI5/Fiori ใƒ™ใƒผใ‚นใฎใƒใƒฃใƒƒใƒˆ UI ใ‹ใ‚‰ๅ‘ผใณๅ‡บใ›ใ‚‹ใ‚ˆใ†ใซใ—ใพใ™ใ€‚

SAP UI5 ใฏ SAP ใŒๆไพ›ใ™ใ‚‹ UI ใƒ•ใƒฌใƒผใƒ ใƒฏใƒผใ‚ฏใงใ‚ใ‚Šใ€ไผๆฅญๅ‘ใ‘ใฎใƒขใƒ€ใƒณใช Web ใ‚ขใƒ—ใƒชใ‚ฑใƒผใ‚ทใƒงใƒณใ‚’็Ÿญๆ™‚้–“ใง้–‹็™บใงใใ‚‹็‚นใŒ็‰นๅพดใงใ™ใ€‚็‰นใซ SAP Business Application Studio๏ผˆBAS๏ผ‰ ใ‚’ๅˆฉ็”จใ™ใ‚‹ใจใ€Fiori ็”จใƒ†ใƒณใƒ—ใƒฌใƒผใƒˆใ‹ใ‚‰ใƒ—ใƒญใ‚ธใ‚งใ‚ฏใƒˆๆง‹ๆˆใ‚’่‡ชๅ‹•็”Ÿๆˆใงใใ‚‹ใŸใ‚ใ€ใƒ‡ใ‚ฃใƒฌใ‚ฏใƒˆใƒชใ‚„่จญๅฎšใƒ•ใ‚กใ‚คใƒซใฎๆ‰‹ๅ‹•ไฝœๆˆใŒไธ่ฆใซใชใ‚Šใพใ™ใ€‚

ย 

2 | ไบ‹ๅ‰ๆบ–ๅ‚™

  • BTP ใ‚ตใƒ–ใ‚ขใ‚ซใ‚ฆใƒณใƒˆ
  • SAP AI Core ใ‚คใƒณใ‚นใ‚ฟใƒณใ‚น
  • SAP AI Launchpadใฎใ‚ตใƒ–ใ‚นใ‚ฏใƒชใƒ—ใ‚ทใƒงใƒณ
  • Python 3.13็’ฐๅขƒ & pip
  • VSCodeใ‚„BASใชใฉใฎIDE

Trial ็’ฐๅขƒใฎๆณจๆ„
Trial ใฎ HANAโ€ฏCloud ใ‚คใƒณใ‚นใ‚ฟใƒณใ‚นใฏย ๆฏŽๆ™ฉ่‡ชๅ‹•ๅœๆญข ใ•ใ‚Œใพใ™ใ€‚ๆ—ฅใ‚’ใพใŸใไฝœๆฅญใฎๅ ดๅˆใฏ็ฟŒๆ—ฅใ‚คใƒณใ‚นใ‚ฟใƒณใ‚นใ‚’ๅ†่ตทๅ‹•ใ—ใฆใใ ใ•ใ„

ย 

3 | Fiori ใ‚ขใƒ—ใƒชใ‚ฑใƒผใ‚ทใƒงใƒณใฎๆบ–ๅ‚™

SAP ็ณป UI ้–‹็™บใงใฏใ€BAS ใฎใƒ†ใƒณใƒ—ใƒฌใƒผใƒˆๆฉŸ่ƒฝใฎๅˆฉ็”จใ‚’ๅผทใๆŽจๅฅจใ—ใพใ™ใ€‚ใƒ†ใƒณใƒ—ใƒฌใƒผใƒˆใ‹ใ‚‰็”Ÿๆˆใ•ใ‚Œใ‚‹ใƒ•ใ‚ฉใƒซใƒ€ๆง‹ๆˆใจ่จญๅฎšใƒ•ใ‚กใ‚คใƒซใ‚’ใƒ™ใƒผใ‚นใซใ™ใ‚‹ใ“ใจใงใ€Fiori ใ‚ขใƒ—ใƒชๅ›บๆœ‰ใฎใƒžใƒ‹ใƒ•ใ‚งใ‚นใƒˆ๏ผˆmta.yaml๏ผ‰ใ‚„ใƒขใ‚ธใƒฅใƒผใƒซๆง‹ๆˆใ‚’็ด ๆ—ฉใๆ•ดๅ‚™ใงใใพใ™ใ€‚

ย 

BAS ใง Create Dev Space ใ‚’ใ‚ฏใƒชใƒƒใ‚ฏใ—ใ€ใƒ†ใƒณใƒ—ใƒฌใƒผใƒˆใซ SAP Fiori ใ‚’้ธๆŠžใ—ใพใ™ใ€‚ไฝตใ›ใฆๅ‰ๅ›žใซไฝœๆˆใ—ใŸ Python API ใ‚’ใƒญใƒผใ‚ซใƒซใง่ตทๅ‹•ใ—ใŸใ„ใฎใงใ€Additional SAP Extensions ใ‹ใ‚‰ Python Tools ใ‚’้ธๆŠžใ—ใพใ™ใ€‚

Screenshot 2025-06-10 at 16.58.38.png

ๅทฆไธŠใฎใƒใƒณใƒใƒผใ‚ฌใƒผใƒกใƒ‹ใƒฅใƒผ โ†’ File > New Project From Template ใ‚’้ธๆŠžใ—ใพใ™ใ€‚

Screenshot 2025-06-07 at 15.54.13.png

Template Selection ใฏ Basic ใ‚’้ธๆŠžใ—ใพใ™ใ€‚

Screenshot 2025-06-07 at 15.53.57.png

Data Source and Service Selection ใงใฏใ€UI ใซ่กจ็คบใ™ใ‚‹ใƒ‡ใƒผใ‚ฟใฏ Python API ใ‹ใ‚‰ๅ–ๅพ—ใ™ใ‚‹ใŸใ‚ None ใ‚’้ธๆŠžใ—ใพใ™ใ€‚

Screenshot 2025-06-07 at 15.55.09.png

Entity Selection ใง Entity name ใซ ChatEntity ใจๅ…ฅๅŠ›ใ—ใพใ™ใ€‚

Screenshot 2025-06-07 at 15.57.51.png

Project Attributes ใ‚’ไปฅไธ‹ใฎใ‚ˆใ†ใซ่จญๅฎšใ—ใ€Add deployment configuration ใ‚’ Yes ใซใ—ใพใ™ใ€‚

้ …็›ฎ ๅ€ค
Module Name my-ai-agent-ui
Application Title My AI Chat
Description Chat UI for AI Agent

Screenshot 2025-06-11 at 13.59.53.png

Deployment Configuration ใงใฏๆฌกใฎใ‚ˆใ†ใซ่จญๅฎšใ—ใพใ™ใ€‚

้ …็›ฎ ๅ€ค
Target Cloud Foundry
Destination None
Add Router Module Add Application to Managed Application Router

Screenshot 2025-06-11 at 14.00.18.png

ใพใ‚Œใซ Target ใ‚’ CF ใซใ—ใฆใ‚‚ Router Module ใฎใ‚ชใƒ—ใ‚ทใƒงใƒณใŒ่กจ็คบใ•ใ‚Œใชใ„ใ“ใจใŒใ‚ใ‚Šใพใ™ใ€‚ใใฎๅ ดๅˆใฏใ€ไธ€ๅบฆ Target ใ‚’ ABAP ใซๅˆ‡ใ‚Šๆ›ฟใˆใฆใ‹ใ‚‰ Cloud Foundry ใซๆˆปใ™ใจๆญฃใ—ใ่กจ็คบใ•ใ‚Œใพใ™ใ€‚

ย 

ๆœ€ๅพŒใซใ€Python API ใฎ้…็ฝฎใจๅ‹•ไฝœ็ขบ่ชใ‚’ๅฎŸๆ–ฝใ—ใพใ™ใ€‚ๆง‹ๆˆใฏๆฌกใฎใ‚ˆใ†ใซใชใ‚Šใพใ™ใ€‚

# ใƒ•ใ‚ฉใƒซใƒ€ๆง‹ๆˆ
PROJECTS
โ”œโ”€โ”€ my-ai-agent-ui/
โ”œโ”€โ”€ my-ai-agent-api/
โ”œโ”€โ”€ main.py
โ”œโ”€โ”€ requirements.txt
โ””โ”€โ”€ .env

my-ai-agent-apiย ้…ไธ‹ใงไปฎๆƒณ็’ฐๅขƒใ‚’ไฝœๆˆใƒปๆœ‰ๅŠนๅŒ–ใ—ใ€ไปฅไธ‹ใฎใ‚ณใƒžใƒณใƒ‰ใงใƒญใƒผใ‚ซใƒซใ‚ตใƒผใƒใŒ่ตทๅ‹•ใ™ใ‚‹ใ‹็ขบ่ชใ—ใพใ™ใ€‚

gunicorn -w 1 -k uvicorn.workers.UvicornWorker main:app –bind 0.0.0.0:${PORT:-8000}

ใ“ใ‚Œใงใ€Fioriใ‚ขใƒ—ใƒชใ‚ฑใƒผใ‚ทใƒงใƒณใฎๆง‹็ฏ‰ๆบ–ๅ‚™ใŒๆ•ดใ„ใพใ—ใŸ๏ผ

ย 

4 | Fiori ใ‚ขใƒ—ใƒชใ‚ฑใƒผใ‚ทใƒงใƒณใฎๆ”น่‰ฏ

็”Ÿๆˆใ•ใ‚ŒใŸ Fiori ใƒ—ใƒญใ‚ธใ‚งใ‚ฏใƒˆใ‚’ใƒ™ใƒผใ‚นใซใ€ใƒใƒฃใƒƒใƒˆ UI ใŒ AI Agent ใจ้€šไฟกใงใใ‚‹ใ‚ˆใ†ใซใ‚ณใƒผใƒ‰ใ‚’่ชฟๆ•ดใ—ใฆใ„ใใพใ—ใ‚‡ใ†ใ€‚

ใพใšใ‚ณใƒณใƒˆใƒญใƒผใƒฉใƒผ๏ผˆmy-ai-agent-ui/webapp/controller/ChatEntity.controller.js๏ผ‰ใ‚’ไปฅไธ‹ใฎใ‚ˆใ†ใซๆ›ธใใพใ™ใ€‚

sap.ui.define([
“sap/ui/core/mvc/Controller”,
“sap/ui/model/json/JSONModel”,
“sap/m/MessageToast”,
“sap/ui/core/BusyIndicator”
], function (Controller, JSONModel, MessageToast, BusyIndicator) {
“use strict”;

// ็’ฐๅขƒใซใ‚ˆใฃใฆใ‚จใƒณใƒ‰ใƒใ‚คใƒณใƒˆใ‚’ๅˆ‡ใ‚Šๆ›ฟใˆ
const ENDPOINT = (
[“localhost”, “applicationstudio”].some(h => window.location.hostname.includes(h)) ||
window.location.port === “8080”
) ? “” : “https://my-ai-agent-api-relaxed-raven-ie.cfapps.us10-001.hana.ondemand.com”;

return Controller.extend(“myaiagentui.controller.ChatEntity”, {
/** ๅˆๆœŸๅŒ– */
onInit() {
this.getView().setModel(new JSONModel({
busy: false,
txtInput: “”,
uploadedFiles: [],
messages: [{
role: “assistant”,
content: “ใ“ใ‚“ใซใกใฏ๏ผ็งใฏใƒใƒฃใƒƒใƒˆใƒœใƒƒใƒˆใงใ™ใ€‚ไฝ•ใ‹ใŠๆ‰‹ไผใ„ใงใใ‚‹ใ“ใจใฏใ‚ใ‚Šใพใ™ใ‹๏ผŸ”,
hasThinkingProcess: false
}]
}), “ui”);

this.byId(“fileUploader”)?.setUploadUrl(`${ENDPOINT}/agent/upload`);
},

/** ใƒใƒฃใƒƒใƒˆๅฑฅๆญดใ‚’ๅˆๆœŸๅŒ– */
onClearChatPress() {
this.getView().getModel(“ui”).setProperty(“/messages”, [{
role: “assistant”,
content: “ใ“ใ‚“ใซใกใฏ๏ผ็งใฏใƒใƒฃใƒƒใƒˆใƒœใƒƒใƒˆใงใ™ใ€‚ไฝ•ใ‹ใŠๆ‰‹ไผใ„ใงใใ‚‹ใ“ใจใฏใ‚ใ‚Šใพใ™ใ‹๏ผŸ”,
hasThinkingProcess: false
}]);
MessageToast.show(“ใƒใƒฃใƒƒใƒˆใ‚’ใ‚ฏใƒชใ‚ขใ—ใพใ—ใŸ”);
},

/** ใƒกใƒƒใ‚ปใƒผใ‚ธ้€ไฟก */
async onBtnChatbotSendPress(){
const ui=this.getView().getModel(“ui”);
const input=(ui.getProperty(“/txtInput”)||””).trim();
if(!input) return;
const msgs=ui.getProperty(“/messages”);
msgs.push({role:”user”, content:input});
ui.setProperty(“/messages”, msgs);
ui.setProperty(“/txtInput”, “”);
ui.setProperty(“/busy”, true);
try{
const {output, intermediate_steps=[]}=await this._apiChatCompletion(input);
msgs.push({role:”assistant”, content:output, hasThinkingProcess:Boolean(intermediate_steps.length), thinkingProcess:intermediate_steps.map((s,i)=>({…s, stepIndex:i+1, observationTruncated:s.observation?.slice(0,100)+(s.observation?.length>100?”…”:””), observationFull:s.observation, isObservationExpanded:false, hasLongObservation:(s.observation?.length||0)>100})), isExpanded:false});
ui.setProperty(“/messages”, msgs);
this._scrollToBottom();
}catch(err){
console.error(err);
MessageToast.show(`ใ‚จใƒฉใƒผ: ${err.message}`);
}finally{
ui.setProperty(“/busy”, false);
}
},

/** ๆ€่€ƒใƒ—ใƒญใ‚ปใ‚น่กจ็คบๅˆ‡ๆ›ฟ */
onToggleThinkingProcess(oEvent) {
const ctx = oEvent.getSource()?.getBindingContext(“ui”);
if (ctx) this._toggleFlag(ctx, “isExpanded”);
},

/** Observation ่กจ็คบๅˆ‡ๆ›ฟ */
onToggleObservation(oEvent) {
const ctx = oEvent.getSource()?.getBindingContext(“ui”);
if (ctx) this._toggleFlag(ctx, “isObservationExpanded”);
},

/** ไปปๆ„ใƒ•ใƒฉใ‚ฐใ‚’ๅ่ปข */
_toggleFlag(ctx, flag) {
const ui = this.getView().getModel(“ui”);
const path = ctx.getPath();
ui.setProperty(`${path}/${flag}`, !ui.getProperty(`${path}/${flag}`));
},

/** ใƒใƒฃใƒƒใƒˆใ‚’ๆœ€ไธ‹้ƒจใธใ‚นใ‚ฏใƒญใƒผใƒซ */
_scrollToBottom() {
setTimeout(() => {
const sc = this.byId(“chatScrollContainer”);
const items = sc?.getContent()[0].getItems();
if (items?.length) sc.scrollToElement(items[items.length – 1]);
}, 100);
},

/** AI ใƒใƒฃใƒƒใƒˆ API ๅ‘ผใณๅ‡บใ— */
async _apiChatCompletion(query) {
const res = await fetch(`${ENDPOINT}/agent/chat`, {
method: “POST”,
headers: { “Content-Type”: “application/json” },
body: JSON.stringify({ query })
});
if (!res.ok) throw new Error(`ใ‚ตใƒผใƒใƒผใ‚จใƒฉใƒผ (${res.status}): ${await res.text()}`);
return res.json();
}
});
});

ย 

ๆฌกใซ View๏ผˆmy-ai-agent-ui/webapp/view/ChatEntity.view.xml๏ผ‰ใ‚’ไปฅไธ‹ใฎใ‚ˆใ†ใซๅค‰ๆ›ดใ—ใพใ™ใ€‚

<mvc:View controllerName=”myaiagentui.controller.ChatEntity”
xmlns:core=”sap.ui.core”
xmlns:mvc=”sap.ui.core.mvc”
xmlns:f=”sap.f”
xmlns:u=”sap.ui.unified”
displayBlock=”true”
xmlns=”sap.m”>

<f:DynamicPage id=”mainDynamicPage” stickySubheaderProvider=”iconTabBar” class=”sapUiNoContentPadding”>
<f:title>
<f:DynamicPageTitle id=”mainDynamicTitle”>
<f:heading>
<Title id=”pageTitle” text=”AI ใƒใƒฃใƒƒใƒˆ with Fiori”/>
</f:heading>
<f:actions>
<Button id=”clearChatButton”
text=”ใƒใƒฃใƒƒใƒˆใ‚’ใ‚ฏใƒชใ‚ข”
icon=”sap-icon://refresh”
type=”Emphasized”
press=”onClearChatPress”/>
</f:actions>
</f:DynamicPageTitle>
</f:title>

<f:content>
<!– ใƒใƒฃใƒƒใƒˆ UI ใƒกใ‚คใƒณใ‚จใƒชใ‚ข –>
<VBox id=”chatMainAreaBox” class=”sapUiNoContentPadding chatMainArea”>
<IconTabBar id=”iconTabBar”
class=”sapUiNoContentPadding”
stretchContentHeight=”true”
expanded=”true”
expandable=”false”>

<items>
<IconTabFilter id=”chatbotTab” text=”ใƒใƒฃใƒƒใƒˆใƒœใƒƒใƒˆ” class=”sapUiNoContentPadding”>
<VBox id=”chatContainerBox” class=”chatContainer sapUiNoContentPadding”>
<!– ใƒใƒฃใƒƒใƒˆใƒกใƒƒใ‚ปใƒผใ‚ธใ‚จใƒชใ‚ข –>
<ScrollContainer id=”chatScrollContainer”
height=”100%”
width=”100%”
vertical=”true”
focusable=”true”
class=”chatScrollContainer”>
<VBox id=”chatMessagesBox” items=”{ui>/messages}” class=”chatMessagesContainer”>
<VBox id=”messageItemBox”>
<!– assistant ใ‚ณใƒกใƒณใƒˆ –>
<VBox id=”assistantMessageVBox” visible=”{= ${ui>role} === ‘assistant’}”
class=”chatMessageWrapper”>
<HBox id=”assistantMessageHBox” direction=”Row” class=”chatMessageRow”>
<HBox id=”assistantMessageHBox1″ backgroundDesign=”Solid”
class=”chatBubbleAssistant”>
<core:Icon id=”assistantMessageIcon” decorative=”true”
src=”sap-icon://ai”
class=”chatIcon”/>
<FormattedText id=”assistantMessageFormattedText” htmlText=”{ui>content}”/>
</HBox>
</HBox>

<!– ๆ€่€ƒใƒ—ใƒญใ‚ปใ‚นใ‚ขใ‚ณใƒผใƒ‡ใ‚ฃใ‚ชใƒณ –>
<HBox id=”thinkingToggleBox” visible=”{ui>hasThinkingProcess}”
class=”thinkingProcessContainer”>
<Button id=”thinkingToggleButton” icon=”{= ${ui>isExpanded} ? ‘sap-icon://collapse’ : ‘sap-icon://expand’ }”
text=”ๆ€่€ƒใƒ—ใƒญใ‚ปใ‚นใ‚’{= ${ui>isExpanded} ? ‘้š ใ™’ : ‘่กจ็คบ’ }”
type=”Transparent”
press=”onToggleThinkingProcess”
class=”thinkingProcessToggle”/>
</HBox>

<VBox id=”thinkingVBox” visible=”{= ${ui>isExpanded} &amp;&amp; ${ui>hasThinkingProcess} }”
class=”thinkingProcessContent”>
<VBox id=”thinkingVBox1″ items=”{ui>thinkingProcess}”>
<VBox id=”thinkingStepBox” class=”thinkingStepBox”>
<Title id=”thinkingStepTitle”
text=”{= ‘Step ‘ + ${ui>stepIndex} }”
level=”H5″
class=”thinkingStepTitle”/>
<VBox id=”thinkingStepContent” class=”thinkingStepContent”>
<HBox id=”thinkingStepThought” class=”thinkingStepItem”>
<Label id=”ThoughtLabel” text=”Thought:” design=”Bold” class=”thinkingLabel”/>
<Text id=”ThoughtText” text=”{ui>thought}” wrapping=”true” class=”thinkingText”/>
</HBox>
<HBox id=”thinkingStepAction” class=”thinkingStepItem”>
<Label id=”ActionLabel” text=”Action:” design=”Bold” class=”thinkingLabel”/>
<Text id=”ActionText” text=”{ui>action}” class=”thinkingText”/>
</HBox>
<HBox id=”thinkingStepInput” class=”thinkingStepItem”>
<Label id=”InputLabel” text=”Input:” design=”Bold” class=”thinkingLabel”/>
<Text id=”InputText” text=”{ui>action_input}” class=”thinkingText”/>
</HBox>
<HBox id=”thinkingStepObservation” class=”thinkingStepItem”>
<Label id=”ObservationLabel” text=”Observation:” design=”Bold” class=”thinkingLabel”/>
<VBox id=”ObservationVBox” class=”thinkingText”>
<Text id=”ObservationText” text=”{= ${ui>isObservationExpanded} ? ${ui>observationFull} : ${ui>observationTruncated} }”
wrapping=”true”/>
<Link id=”ObservationLink”
text=”{= ${ui>isObservationExpanded} ? ‘ๆŠ˜ใ‚ŠใŸใŸใ‚€’ : ‘็ถšใใ‚’่ชญใ‚€’ }”
visible=”{ui>hasLongObservation}”
press=”onToggleObservation”
class=”observationToggleLink”/>
</VBox>
</HBox>
</VBox>
</VBox>
</VBox>
</VBox>
</VBox>

<!– user ใ‚ณใƒกใƒณใƒˆ –>
<HBox id=”UserComment”
visible=”{= ${ui>role} === ‘user’}”
direction=”RowReverse”
class=”chatMessageRow”>
<HBox id=”UserCommentBox”
direction=”RowReverse”
backgroundDesign=”Solid”
class=”chatBubbleUser sapThemeBrand-asBackgroundColor”>
<core:Icon id=”UserCommentIcon”
decorative=”true”
src=”sap-icon://customer”
class=”chatIcon sapThemeTextInverted”/>
<Text id=”UserCommentText” class=”sapThemeTextInverted” text=”{ui>content}”/>
</HBox>
</HBox>
</VBox>
</VBox>
</ScrollContainer>

<!– ๅ…ฅๅŠ›ๆฌ„๏ผ‹้€ไฟกใƒœใ‚ฟใƒณ –>
<HBox id=”chatInputAreaBox” class=”chatInputArea”>
<TextArea id=”chatInput” value=”{ui>/txtInput}”
width=”100%”
growing=”true”
placeholder=”ใƒกใƒƒใ‚ปใƒผใ‚ธใ‚’ๅ…ฅๅŠ›ใ—ใฆใใ ใ•ใ„…”
editable=”{= !${ui>/busy}}”
busyIndicatorDelay=”0″
class=”chatTextArea”>
<layoutData>
<FlexItemData id=”InputData” growFactor=”1″/>
</layoutData>
</TextArea>

<Button id=”sendButton” class=”chatSendButton”
type=”Emphasized”
icon=”sap-icon://paper-plane”
text=”้€ไฟก”
press=”onBtnChatbotSendPress”
busy=”{ui>/busy}”
busyIndicatorDelay=”0″/>
</HBox>
</VBox>
</IconTabFilter>
</items>
</IconTabBar>
</VBox>
</f:content>
</f:DynamicPage>
</mvc:View>

ย 

ใ•ใ‚‰ใซใ€UI ใฎ่ฉณ็ดฐใชใƒ‡ใ‚ถใ‚คใƒณ่จญๅฎšใจใ—ใฆใ€ใ‚นใ‚ฟใ‚คใƒซ๏ผˆmy-ai-agent-ui/webapp/css/style.css๏ผ‰ใ‚’่จญๅฎšใ—ใพใ™ใ€‚

/* ใƒใƒฃใƒƒใƒˆใƒกใ‚คใƒณใ‚จใƒชใ‚ข */
.chatMainArea {
height: 100vh;
min-height: 300px;
}

/* ใƒใƒฃใƒƒใƒˆใ‚ณใƒณใƒ†ใƒŠ */
.chatContainer {
display: flex;
flex-direction: column;
height: calc(90vh – 100px);
padding: 0;
}

/* ใƒใƒฃใƒƒใƒˆใ‚นใ‚ฏใƒญใƒผใƒซใ‚ณใƒณใƒ†ใƒŠ */
.chatScrollContainer {
flex: 1;
min-height: 300px;
background-color: #f8f9fa;
border: none;
border-radius: 0;
padding: 0;
margin: 0;
}

/* ใƒใƒฃใƒƒใƒˆใƒกใƒƒใ‚ปใƒผใ‚ธใ‚ณใƒณใƒ†ใƒŠ */
.chatMessagesContainer {
padding: 1rem;
}

/* ใƒใƒฃใƒƒใƒˆใƒกใƒƒใ‚ปใƒผใ‚ธ่กŒ */
.chatMessageRow {
width: 100%;
margin-bottom: 0.5rem;
}

/* ใƒใƒฃใƒƒใƒˆใƒกใƒƒใ‚ปใƒผใ‚ธใƒฉใƒƒใƒ‘ใƒผ */
.chatMessageWrapper {
margin-bottom: 1rem;
}

/* ใƒใƒฃใƒƒใƒˆใ‚ณใƒกใƒณใƒˆ – ใ‚ขใ‚ทใ‚นใ‚ฟใƒณใƒˆ */
.chatBubbleAssistant {
max-width: 70%;
min-width: 100px;
background-color: #ffffff;
border: 1px solid #d0d7de;
border-radius: 18px;
padding: 12px 16px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
align-items: flex-start;
}

.chatBubbleAssistant .sapMFormattedText {
word-wrap: break-word;
line-height: 1.4;
}

/* ใƒใƒฃใƒƒใƒˆใ‚ณใƒกใƒณใƒˆ – ใƒฆใƒผใ‚ถใƒผ */
.chatBubbleUser {
max-width: 70%;
min-width: 100px;
border-radius: 18px;
padding: 12px 16px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
align-items: flex-start;
}

.chatBubbleUser .sapMText {
word-wrap: break-word;
line-height: 1.4;
}

/* ๆ€่€ƒใƒ—ใƒญใ‚ปใ‚นใ‚ณใƒณใƒ†ใƒŠ */
.thinkingProcessContainer {
margin-top: 0.5rem;
margin-left: 2.5rem;
}

/* ๆ€่€ƒใƒ—ใƒญใ‚ปใ‚นใƒˆใ‚ฐใƒซใƒœใ‚ฟใƒณ */
.thinkingProcessToggle {
font-size: 0.875rem;
color: #0073e6;
}

.thinkingProcessToggle:hover {
background-color: rgba(0, 115, 230, 0.08) !important;
}

/* ๆ€่€ƒใƒ—ใƒญใ‚ปใ‚นใ‚ณใƒณใƒ†ใƒณใƒ„ */
.thinkingProcessContent {
margin-left: 2.5rem;
margin-top: 0.5rem;
max-width: 70%;
}

/* ๆ€่€ƒใ‚นใƒ†ใƒƒใƒ—ใƒœใƒƒใ‚ฏใ‚น */
.thinkingStepBox {
margin-bottom: 1.5rem;
padding: 1rem;
background-color: #f5f7fa;
border: 1px solid #e4e7ea;
border-radius: 8px;
}

.thinkingStepBox:last-child {
margin-bottom: 0;
}

/* ๆ€่€ƒใ‚นใƒ†ใƒƒใƒ—ใ‚ฟใ‚คใƒˆใƒซ */
.thinkingStepTitle {
color: #0a6ed1;
margin-bottom: 0.75rem;
font-size: 1rem;
}

/* ๆ€่€ƒใ‚นใƒ†ใƒƒใƒ—ใ‚ณใƒณใƒ†ใƒณใƒ„ */
.thinkingStepContent {
padding-left: 0.5rem;
}

/* ๆ€่€ƒใ‚นใƒ†ใƒƒใƒ—้ …็›ฎ */
.thinkingStepItem {
margin-bottom: 0.5rem;
align-items: flex-start;
}

.thinkingStepItem:last-child {
margin-bottom: 0;
}

/* ๆ€่€ƒใƒฉใƒ™ใƒซ */
.thinkingLabel {
min-width: 100px;
margin-right: 1rem;
color: #32363a;
font-size: 0.875rem;
}

/* ๆ€่€ƒใƒ†ใ‚ญใ‚นใƒˆ */
.thinkingText {
flex: 1;
font-size: 0.875rem;
color: #515456;
line-height: 1.5;
}

/* Observation็ถšใใ‚’่ชญใ‚€ใƒชใƒณใ‚ฏ */
.observationToggleLink {
margin-top: 0.25rem;
font-size: 0.875rem;
}

/* ๅ…ฅๅŠ›ใ‚จใƒชใ‚ข */
.chatInputArea {
background-color: #ffffff;
padding: 1rem;
border-top: 1px solid #e4e7ea;
align-items: flex-end;
flex-shrink: 0;
}

/* ใƒ†ใ‚ญใ‚นใƒˆใ‚จใƒชใ‚ขใฎใ‚นใ‚ฟใ‚คใƒซ่ชฟๆ•ด */
.chatTextArea .sapMTextAreaInner {
border: 2px solid #e4e7ea;
background-color: #ffffff;
}

.chatTextArea .sapMTextAreaInner:focus-within {
border-color: #0073e6;
}

/* ้€ไฟกใƒœใ‚ฟใƒณ */
.chatSendButton {
min-height: 40px;
border-radius: 20px;
margin-left: 0.5rem;
}

/* ใ‚ขใ‚คใ‚ณใƒณ่ชฟๆ•ด */
.chatIcon {
font-size: 1.2rem;
margin-top: 2px;
}

.chatBubbleAssistant .chatIcon {
margin-right: 0.5rem;
}

.chatBubbleUser .chatIcon {
margin-left: 0.5rem;
}

/* IconTabBar ใฎใƒ‘ใƒ‡ใ‚ฃใƒณใ‚ฐๅ‰Š้™ค */
.sapMITB {
padding: 0;
}

.sapMITBContent {
padding: 0;
background-color: transparent;
}

/* ใƒฌใ‚นใƒใƒณใ‚ทใƒ–ๅฏพๅฟœ */
@media (max-width: 768px) {
.chatBubbleAssistant,
.chatBubbleUser {
max-width: 85%;
}

.chatContainer {
height: calc(100vh – 80px);
}

.chatScrollContainer {
min-height: 300px;
}

.chatSendButton .sapMBtnInner .sapMBtnContent {
font-size: 0;
}

.chatSendButton .sapMBtnIcon {
margin-right: 0;
}

.thinkingProcessContent {
max-width: 85%;
}

.thinkingLabel {
min-width: 80px;
margin-right: 0.5rem;
}
}

ย 

ใƒใƒฃใƒƒใƒˆ UI ใ‹ใ‚‰ Python API ใธใƒชใ‚ฏใ‚จใ‚นใƒˆใ‚’่ปข้€ใ™ใ‚‹ใŸใ‚ใ€ใƒ—ใƒญใ‚ญใ‚ทใƒŸใƒ‰ใƒซใ‚ฆใ‚งใ‚ขใ‚’่ฟฝๅŠ ใ—ใพใ™ใ€‚ใพใšไพๅญ˜้–ขไฟ‚ใ‚’ใ‚คใƒณใ‚นใƒˆใƒผใƒซใ—ใพใ—ใ‚‡ใ†ใ€‚

npm install ui5-middleware-simpleproxy –save-dev

ย 

ใใ—ใฆ my-ai-agent-ui/package.json ใซๆฌกใฎ่จญๅฎšใ‚’่ฟฝ่จ˜ใ—ใฆใใ ใ•ใ„ใ€‚

…
“main”: “webapp/index.html”,
“devDependencies”: {
(็œ็•ฅ)
},
/* ใ“ใ“ใ‹ใ‚‰
“ui5”: {
“dependencies”: [
“ui5-middleware-simpleproxy”
ใ€€ใ€€]
},
ใ€€ใ“ใ“ใพใง */
“scripts”: {
….

ใ“ใ‚Œใงใƒใƒฃใƒƒใƒˆ UI ใ‹ใ‚‰ใฎ /api ใƒชใ‚ฏใ‚จใ‚นใƒˆใŒใƒญใƒผใ‚ซใƒซใฎ Python API ใธ่ปข้€ใ•ใ‚Œใ‚‹ใ‚ˆใ†ใซใชใ‚Šใพใ™ใ€‚

ย 

5 | Fiori ใ‚ขใƒ—ใƒชใ‚ฑใƒผใ‚ทใƒงใƒณใฎใƒ†ใ‚นใƒˆ

ใƒญใƒผใ‚ซใƒซ็’ฐๅขƒใงใ‚ขใƒ—ใƒชใจ API ใ‚’ๅŒๆ™‚ใซ่ตทๅ‹•ใ—ใ€ๅฎŸ้š›ใซใƒใƒฃใƒƒใƒˆๆฉŸ่ƒฝใŒๅ‹•ไฝœใ™ใ‚‹ใ‹็ขบ่ชใ—ใพใ—ใ‚‡ใ†ใ€‚

ใพใšใ€my-ai-agent-api ใƒ‡ใ‚ฃใƒฌใ‚ฏใƒˆใƒชใงไปฅไธ‹ใฎใ‚ณใƒžใƒณใƒ‰ใ‚’ๅฎŸ่กŒใ—ใฆ Python API ใ‚’่ตทๅ‹•ใ—ใพใ™ใ€‚

gunicorn -w 1 -k uvicorn.workers.UvicornWorker main:app –bind 0.0.0.0:${PORT:-8000}

ใพใŸใ€ๅˆฅใฎใ‚ฟใƒผใƒŸใƒŠใƒซใ‚’้–‹ใใ€my-ai-agent-ui ใฎใƒซใƒผใƒˆใงๆฌกใฎใ‚ณใƒžใƒณใƒ‰ใ‚’ๅฎŸ่กŒใ—ใพใ™ใ€‚

npm start

Screenshot 2025-06-12 at 21.19.19.png

ย 

ใƒญใ‚ฐใซ่กจ็คบใ•ใ‚ŒใŸ URL๏ผˆ้€šๅธธใฏ http://localhost:8080๏ผ‰ใ‚’ใƒ–ใƒฉใ‚ฆใ‚ถใง้–‹ใใ€ใƒใƒฃใƒƒใƒˆๅ…ฅๅŠ›ๆฌ„ใซใƒกใƒƒใ‚ปใƒผใ‚ธใ‚’้€ใฃใฆใฟใฆใใ ใ•ใ„ใ€‚AI ใ‚จใƒผใ‚ธใ‚งใƒณใƒˆใ‹ใ‚‰่ฟ”็ญ”ใŒ่กจ็คบใ•ใ‚Œใ‚ŒใฐๆˆๅŠŸใงใ™๏ผ

Screenshot 2025-06-12 at 21.22.04.png

ย 

ใƒใƒฃใƒƒใƒˆ็”ป้ขใฎ ใƒใƒฃใƒƒใƒˆใ‚’ใ‚ฏใƒชใ‚ข ใƒœใ‚ฟใƒณใ‚’ใ‚ฏใƒชใƒƒใ‚ฏใ™ใ‚‹ใจๅฑฅๆญดใŒใƒชใ‚ปใƒƒใƒˆใ•ใ‚Œใ€ใ€Œใƒใƒฃใƒƒใƒˆใ‚’ใ‚ฏใƒชใ‚ขใ—ใพใ—ใŸใ€ใจ็ขบ่ชใงใใพใ™ใ€‚ๆ–ฐใ—ใ„ไผš่ฉฑใ‚’ๅ•้กŒใชใๅง‹ใ‚ใ‚‰ใ‚Œใ‚‹ใ‹็ขบ่ชใ—ใพใ—ใ‚‡ใ†๏ผ

Screenshot 2025-06-12 at 21.22.42.png

ย 

6 | ใƒใƒฃใƒฌใƒณใ‚ธ โ€“ ใƒ†ใ‚ญใ‚นใƒˆใƒ•ใ‚กใ‚คใƒซใ‚ขใƒƒใƒ—ใƒญใƒผใƒ‰ใƒœใ‚ฟใƒณใ‚’่ฟฝๅŠ ใ—ใ‚ˆใ†

ใƒ†ใ‚ญใ‚นใƒˆใ‚’่ฟฝๅŠ ใงใ‚ขใƒƒใƒ—ใƒญใƒผใƒ‰ใ—ใฆใ€่ฟฝๅŠ ใ—ใŸใƒ‰ใ‚ญใƒฅใƒกใƒณใƒˆใ‚’่ธใพใˆใŸ AI Agent ใจใฎใ‚„ใ‚Šใจใ‚Šใ‚’ๅฎŸ็พใ—ใฆ่ฆ‹ใพใ—ใ‚‡ใ†ใ€‚ใชใŠใ€ใ“ใฎใƒใƒฃใƒฌใƒณใ‚ธใฏๅ‰ๅ›žใฎใƒใƒฃใƒฌใƒณใ‚ธใ‚’ๅฎŸๆ–ฝใ—ใฆใ„ใชใ„ใจๅฎŸ่ฃ…ใงใใชใ„ใฎใงๆณจๆ„ใ—ใพใ—ใ‚‡ใ†ใ€‚

ใƒใƒฃใƒƒใƒˆ UI ใฎใƒ˜ใƒƒใƒ€ใƒผใซ .txt ใƒ•ใ‚กใ‚คใƒซๅฐ‚็”จใฎใ‚ขใƒƒใƒ—ใƒญใƒผใƒ‰ใƒœใ‚ฟใƒณ ใ‚’่ฟฝๅŠ ใ—ใ€้ธๆŠžใ—ใŸใƒ•ใ‚กใ‚คใƒซใ‚’ Python API ใธ้€ไฟกใ—ใ€ใ‚ตใƒผใƒๅดใงใƒ•ใ‚กใ‚คใƒซใ‚’ใƒใƒฃใƒณใ‚ฏๅŒ–ใ—ใฆไฟๅญ˜ใ™ใ‚‹ใ‚ˆใ†ใซใ—ใพใ™ใ€‚้€”ไธญ็ตŒ้Žใ‚’ใ‚ใ‹ใ‚Šใ‚„ใ™ใใ™ใ‚‹ใŸใ‚ใ€ใ‚ขใƒƒใƒ—ใƒญใƒผใƒ‰ไธญใฏ BusyIndicator ใง็”ป้ขใ‚’ใƒ–ใƒญใƒƒใ‚ฏใ™ใ‚‹ใ‚ˆใ†ใซใ—ใพใ—ใ‚‡ใ†ใ€‚

ย 

ChatEntity.controller.js ใงๆฌกใฎ 3 ็‚นใ‚’่ฟฝๅŠ ใ—ใพใ™๏ผˆใƒ‘ใ‚นใ‚„ๅๅ‰็ฉบ้–“ใฏ็’ฐๅขƒใซๅˆใ‚ใ›ใฆใใ ใ•ใ„๏ผ‰:

  • ๅ†’้ ญใฎ sap/ui/core/BusyIndicator ใ‚คใƒณใƒใƒผใƒˆ
  • onFileChange ใƒใƒณใƒ‰ใƒฉ โ€“ ใƒ•ใ‚กใ‚คใƒซ็จฎๅˆฅใƒใ‚งใƒƒใ‚ฏ & ใ‚ขใƒƒใƒ—ใƒญใƒผใƒ‰ๅ‘ผใณๅ‡บใ—
  • _uploadFile ใƒกใ‚ฝใƒƒใƒ‰ โ€“ fetch + FormData ใง API /agent/upload ใธ POST

/** ใƒ•ใ‚กใ‚คใƒซ้ธๆŠžๆ™‚ */
async onFileChange(oEvent) {
const oFileUploader = oEvent.getSource();
const file = (oEvent.getParameter(“files”) || [])[0];
if (!file) return;
if (!file.name.endsWith(“.txt”)) {
MessageToast.show(“txtๅฝขๅผใฎใƒ•ใ‚กใ‚คใƒซใฎใฟใ‚ขใƒƒใƒ—ใƒญใƒผใƒ‰ๅฏ่ƒฝใงใ™ใ€‚”);
oFileUploader.clear();
return;
}
await this._uploadFile(file);
oFileUploader.clear();
},

/** ใƒ•ใ‚กใ‚คใƒซใ‚ขใƒƒใƒ—ใƒญใƒผใƒ‰ */
async _uploadFile(file) {
const ui = this.getView().getModel(“ui”);
ui.setProperty(“/busy”, true);
BusyIndicator.show(0);
try {
const fd = new FormData();
fd.append(“file”, file);
const res = await fetch(`${ENDPOINT}/agent/upload`, {
method: “POST”,
body: fd
});
if (!res.ok) throw new Error(await res.text());
const { filename, chunks_created } = await res.json();
const files = ui.getProperty(“/uploadedFiles”) || [];
ui.setProperty(“/uploadedFiles”, […files, {
name: filename,
chunks: chunks_created,
uploadedAt: new Date()
}]);
MessageToast.show(`”${filename}” ใ‚’ใ‚ขใƒƒใƒ—ใƒญใƒผใƒ‰ใ—ใพใ—ใŸ๏ผˆ${chunks_created} ใƒใƒฃใƒณใ‚ฏไฝœๆˆ๏ผ‰`);
} catch (err) {
console.error(err);
MessageToast.show(`ใ‚ขใƒƒใƒ—ใƒญใƒผใƒ‰ใ‚จใƒฉใƒผ: ${err.message}`);
} finally {
ui.setProperty(“/busy”, false);
BusyIndicator.hide();
}
}

ย 

ๆฌกใซใ€ใƒ˜ใƒƒใƒ€ใƒผใธ FileUploader ใจ Clear ใƒœใ‚ฟใƒณใ‚’ไธฆในใพใ™ใ€‚FileUploader ใฏ buttonOnly=”true” ใซใ—ใฆใ€ใ‚ขใ‚คใ‚ณใƒณใƒœใ‚ฟใƒณใซใ™ใ‚‹ใฎใŒใ‚ณใƒ„ใงใ™ใ€‚

<f:actions>
<u:FileUploader id=”fileUploader”
name=”file”
placeholder=”ใƒ•ใ‚กใ‚คใƒซใ‚’้ธๆŠž…”
fileType=”txt”
change=”onFileChange”
buttonText=””
buttonOnly=”true”
icon=”sap-icon://upload”
class=”fileUploaderButton” />
<Button id=”clearChatButton”
text=”ใƒใƒฃใƒƒใƒˆใ‚’ใ‚ฏใƒชใ‚ข”
icon=”sap-icon://refresh”
type=”Emphasized”
press=”onClearChatPress” />
</f:actions>

ย 

ใ•ใ‚‰ใซใƒ˜ใƒƒใƒ€ใƒผไธ‹้ƒจใซย “ใ‚ขใƒƒใƒ—ใƒญใƒผใƒ‰ๆธˆใƒ•ใ‚กใ‚คใƒซไธ€่ฆง” ใ‚’ๅ‡บใ™ใ“ใจใงใ€ใฉใฎใƒ•ใ‚กใ‚คใƒซใŒไฝ•ใƒใƒฃใƒณใ‚ฏใซๅˆ†ๅ‰ฒใ•ใ‚ŒใŸใ‹ใ‚’่ฆ–่ฆšๅŒ–ใงใใพใ™ใ€‚

<HBox id=”fileUploadAreaBox”
visible=”{= ${ui>/uploadedFiles}.length > 0}”>
<Text text=”่ฟฝๅŠ ใงใ‚ขใƒƒใƒ—ใƒญใƒผใƒ‰ใ—ใŸใƒ•ใ‚กใ‚คใƒซ:” />
<HBox items=”{ui>/uploadedFiles}”>
<ObjectStatus text=”{ui>name}” icon=”sap-icon://document-text” />
</HBox>
</HBox>

ย 

ไธŠ็ดšใƒใƒฃใƒฌใƒณใ‚ธ โ€“ ใƒชใ‚ปใƒƒใƒˆใƒœใ‚ฟใƒณ

ใƒ•ใ‚กใ‚คใƒซใ‚’ใ‚ขใƒƒใƒ—ใƒญใƒผใƒ‰ใ—ใฆใ„ใใจใƒ™ใ‚ฏใƒˆใƒซ DB ใŒใฉใ‚“ใฉใ‚“่†จใ‚‰ใฟใพใ™ใ€‚ใƒ†ใ‚นใƒˆ็”จใซ ใƒ‡ใƒผใ‚ฟใƒ™ใƒผใ‚นใ‚’ไธ€ๆ‹ฌใƒชใ‚ปใƒƒใƒˆ ใ™ใ‚‹ใƒœใ‚ฟใƒณใ‚’่ฟฝๅŠ ใ—ใฆใฟใพใ—ใ‚‡ใ†ใ€‚

  • API ๅด: /agent/delete ใ‚’ๅฎŸ่ฃ…๏ผˆใƒ’ใƒณใƒˆ: Part4 ใซใŠใ‘ใ‚‹ DB ใ‚’ๅˆๆœŸๅŒ–ใ™ใ‚‹ใ‚ณใƒผใƒ‰๏ผ‰ใ€‚
  • Controller: onDeletePress ใ‚’ๅฎŸ่ฃ…ใ—ใ€/agent/delete ใ‚’ๅ‘ผใณๅ‡บใ—ใฆ UI ใฎใƒ•ใ‚กใ‚คใƒซไธ€่ฆงใ‚‚็ฉบใซใ™ใ‚‹ใ€‚
  • View: ใƒ˜ใƒƒใƒ€ใƒผใฎใ‚ขใƒƒใƒ—ใƒญใƒผใƒ‰ใƒœใ‚ฟใƒณใจใ‚ฏใƒชใ‚ขใƒœใ‚ฟใƒณใฎ้–“ใซ Delete ใƒœใ‚ฟใƒณใ‚’้…็ฝฎใ€‚

ๅฎŸ่ฃ…ใƒ’ใƒณใƒˆ
UI Model ใฎ /uploadedFiles ใ‚’็ฉบ้…ๅˆ— [] ใซใ™ใ‚‹ใจใ€ใƒ•ใ‚กใ‚คใƒซไธ€่ฆงใ‚จใƒชใ‚ขใŒ่‡ชๅ‹•็š„ใซ้ž่กจ็คบใซใชใ‚Šใพใ™ใ€‚โ€ƒ

Screenshot 2025-06-13 at 11.32.45.png

ย 

7 | ๆฌกๅ›žไบˆๅ‘Š

Part 8 CloudFoundry ใซใƒ‡ใƒ—ใƒญใ‚ค

Partโ€ฏ8 ใงใฏใ€ๅฎŒๆˆใ—ใŸใ‚ขใƒ—ใƒชใ‚ฑใƒผใ‚ทใƒงใƒณใ‚’ Cloud Foundry ใธใƒ‡ใƒ—ใƒญใ‚คใ—ใพใ™ใ€‚UI ใฏ mta build โ†’ cf deployใ€Python ใƒใƒƒใ‚ฏใ‚จใƒณใƒ‰ใฏ manifest.yaml ใ‚’ไฝฟใฃใฆ cf push โ€• 2 ๆœฌ็ซ‹ใฆใง โ€œ็ง’้€Ÿใƒ‡ใƒ—ใƒญใ‚คโ€ ใซๆŒ‘ๆˆฆใ—ใพใ™ใ€‚ใŠๆฅฝใ—ใฟใซ๏ผ

ย 

ๅ…่ฒฌไบ‹้ …

ๆœฌใƒ–ใƒญใ‚ฐใซ่จ˜่ผ‰ใ•ใ‚ŒใŸ่ฆ‹่งฃใŠใ‚ˆใณๆ„่ฆ‹ใฏใ™ในใฆ็งๅ€‹ไบบใฎใ‚‚ใฎใงใ‚ใ‚Šใ€็งใฎๅ€‹ไบบ็š„ใช็ซ‹ๅ ดใง็™บไฟกใ—ใฆใ„ใพใ™ใ€‚SAP ใฏๆœฌใƒ–ใƒญใ‚ฐใฎๅ†…ๅฎนใซใคใ„ใฆไธ€ๅˆ‡ใฎ่ฒฌไปปใ‚’่ฒ ใ„ใพใ›ใ‚“ใ€‚

“}]]ย 

ย [[{“value”:”็”Ÿๆˆ AI ใฏ็ ”็ฉถๅฎคใฎๅค–ใธ้ฃ›ใณๅ‡บใ—ใ€ไปŠใ‚„ใƒ“ใ‚ธใƒใ‚น็พๅ ดใฎๅธธ่ญ˜ใ‚’ๅก—ใ‚Šๆ›ฟใˆใฆใ„ใพใ™ใ€‚SAPใฏๅ…จ้€ŸๅŠ›ใงใใฎๆณขใซไน—ใฃใฆใ„ใพใ™ใ€‚ใ“ใฎใƒ–ใƒญใ‚ฐใ‚ทใƒชใƒผใ‚บใงใฏใ€SAPย AIย Core ใฎๆ—ขๅฎšใƒขใƒ‡ใƒซใ‚’ๆœ€้€Ÿใงๅ‘ผใณๅ‡บใ—ใ€ๅฎŸๅ‹™ใงไฝฟใˆใ‚‹ AI ใ‚จใƒผใ‚ธใ‚งใƒณใƒˆใธๆ‹กๅผตใ™ใ‚‹โ€œ็ง’้€Ÿใƒใƒณใ‚บใ‚ชใƒณโ€ใ‚’ใŠๅฑŠใ‘ใ—ใพใ™ใ€‚ใŠ็Ÿฅใ‚‰ใ›You can find the English version here.ย ๐Ÿ“–ย ๆœฌใ‚ทใƒชใƒผใ‚บใงๅญฆในใ‚‹ใ“ใจSAPย AIย Core ไธŠใงใ‚ซใ‚นใ‚ฟใƒ  AI Agent ใ‚’ โ€œ็ง’้€Ÿโ€ ใงๅ‹•ใ‹ใ™ๆ–นๆณ•LangChainใƒปGoogle ๆคœ็ดขใƒ„ใƒผใƒซใƒปRAGใ‚’ไฝฟใฃใŸๅฎŸ่ฃ…AI Agent ใ‚’ RESTย API ๅŒ–ใ—ใ€SAPUI5/Fiori ใฎ UI ใซ่ผ‰ใ›ใ€Cloud Foundryใซใƒ‡ใƒ—ใƒญใ‚คใ™ใ‚‹ๆ‰‹้ †ๅญฆ็ฟ’ๆ™‚้–“ๅ„Part ใฏย 10โ€“15 ๅˆ†ย ใง่ชญใ‚ใ‚‹๏ผ†ๆ‰‹ใ‚’ๅ‹•ใ‹ใ›ใ‚‹ใƒœใƒชใƒฅใƒผใƒ ใ‚’ไบˆๅฎšใ—ใฆใ„ใพใ™ใ€‚ย ๐Ÿ—บ๏ธ ้€ฃ่ผ‰ใƒญใƒผใƒ‰ใƒžใƒƒใƒ—Part 0 ใƒ—ใƒญใƒญใƒผใ‚ฐPart 1 ็’ฐๅขƒๆง‹็ฏ‰: SAP AI CoreใจAI LaunchpadPart 2 LangChain ใงใƒใƒฃใƒƒใƒˆใƒขใƒ‡ใƒซใ‚’ๆง‹็ฏ‰Part 3 Agentใƒ„ใƒผใƒซ: Googleย ๆคœ็ดขใ‚’็ตฑๅˆPart 4 RAG ๅŸบ็คŽ โ‘ : HANA Cloud VectorEngineใจๅŸ‹ใ‚่พผใฟๅ‡ฆ็†Part 5 RAG ๅŸบ็คŽ โ‘ก: Retriever Toolใฎๆง‹็ฏ‰Part 6 AI Agent ใ‚’ RESTย API ๅŒ–Part 7 SAPUI5 ใงใƒใƒฃใƒƒใƒˆUI ใ‚’ๆง‹็ฏ‰ย [็พๅœจใฎใƒ–ใƒญใ‚ฐ]Part 8 CloudFoundry ใซใƒ‡ใƒ—ใƒญใ‚คใ“ใฎ่จ˜ไบ‹ใŒใŠๅฝนใซ็ซ‹ใฃใŸใ‚‰ใ€ใœใฒโ€ฏKudosโ€ฏใ‚’ๆŠผใ—ใฆใ„ใŸใ ใ‘ใ‚‹ใจๅŠฑใฟใซใชใ‚Šใพใ™ใ€‚ ใ€Œใ“ใ“ใ‚’ใ‚‚ใฃใจ่ฉณใ—ใ็Ÿฅใ‚ŠใŸใ„ใ€ใ€Œๅˆฅใฎใƒ†ใƒผใƒžใ‚‚ๅ–ใ‚ŠไธŠใ’ใฆใปใ—ใ„ใ€ใชใฉใ€ใ”่ฆๆœ›ใŒใ‚ใ‚ŒใฐใŠๆฐ—่ปฝใซใ‚ณใƒกใƒณใƒˆใใ ใ•ใ„๏ผSAPUI5 ใงใƒใƒฃใƒƒใƒˆUI ใ‚’ๆง‹็ฏ‰1 | ใฏใ˜ใ‚ใซไปŠๅ›žใฎPart ใงใฏใ€ๅ‰ๅ›žใพใงใซๆง‹็ฏ‰ใ—ใŸ AI Agent ใ‚’ใ€SAP UI5/Fiori ใƒ™ใƒผใ‚นใฎใƒใƒฃใƒƒใƒˆ UI ใ‹ใ‚‰ๅ‘ผใณๅ‡บใ›ใ‚‹ใ‚ˆใ†ใซใ—ใพใ™ใ€‚SAP UI5 ใฏ SAP ใŒๆไพ›ใ™ใ‚‹ UI ใƒ•ใƒฌใƒผใƒ ใƒฏใƒผใ‚ฏใงใ‚ใ‚Šใ€ไผๆฅญๅ‘ใ‘ใฎใƒขใƒ€ใƒณใช Web ใ‚ขใƒ—ใƒชใ‚ฑใƒผใ‚ทใƒงใƒณใ‚’็Ÿญๆ™‚้–“ใง้–‹็™บใงใใ‚‹็‚นใŒ็‰นๅพดใงใ™ใ€‚็‰นใซ SAP Business Application Studio๏ผˆBAS๏ผ‰ ใ‚’ๅˆฉ็”จใ™ใ‚‹ใจใ€Fiori ็”จใƒ†ใƒณใƒ—ใƒฌใƒผใƒˆใ‹ใ‚‰ใƒ—ใƒญใ‚ธใ‚งใ‚ฏใƒˆๆง‹ๆˆใ‚’่‡ชๅ‹•็”Ÿๆˆใงใใ‚‹ใŸใ‚ใ€ใƒ‡ใ‚ฃใƒฌใ‚ฏใƒˆใƒชใ‚„่จญๅฎšใƒ•ใ‚กใ‚คใƒซใฎๆ‰‹ๅ‹•ไฝœๆˆใŒไธ่ฆใซใชใ‚Šใพใ™ใ€‚ย 2 | ไบ‹ๅ‰ๆบ–ๅ‚™BTP ใ‚ตใƒ–ใ‚ขใ‚ซใ‚ฆใƒณใƒˆSAP AI Core ใ‚คใƒณใ‚นใ‚ฟใƒณใ‚นSAP AI Launchpadใฎใ‚ตใƒ–ใ‚นใ‚ฏใƒชใƒ—ใ‚ทใƒงใƒณPython 3.13็’ฐๅขƒ & pipVSCodeใ‚„BASใชใฉใฎIDETrial ็’ฐๅขƒใฎๆณจๆ„Trial ใฎ HANAโ€ฏCloud ใ‚คใƒณใ‚นใ‚ฟใƒณใ‚นใฏย ๆฏŽๆ™ฉ่‡ชๅ‹•ๅœๆญข ใ•ใ‚Œใพใ™ใ€‚ๆ—ฅใ‚’ใพใŸใไฝœๆฅญใฎๅ ดๅˆใฏ็ฟŒๆ—ฅใ‚คใƒณใ‚นใ‚ฟใƒณใ‚นใ‚’ๅ†่ตทๅ‹•ใ—ใฆใใ ใ•ใ„ย 3 | Fiori ใ‚ขใƒ—ใƒชใ‚ฑใƒผใ‚ทใƒงใƒณใฎๆบ–ๅ‚™SAP ็ณป UI ้–‹็™บใงใฏใ€BAS ใฎใƒ†ใƒณใƒ—ใƒฌใƒผใƒˆๆฉŸ่ƒฝใฎๅˆฉ็”จใ‚’ๅผทใๆŽจๅฅจใ—ใพใ™ใ€‚ใƒ†ใƒณใƒ—ใƒฌใƒผใƒˆใ‹ใ‚‰็”Ÿๆˆใ•ใ‚Œใ‚‹ใƒ•ใ‚ฉใƒซใƒ€ๆง‹ๆˆใจ่จญๅฎšใƒ•ใ‚กใ‚คใƒซใ‚’ใƒ™ใƒผใ‚นใซใ™ใ‚‹ใ“ใจใงใ€Fiori ใ‚ขใƒ—ใƒชๅ›บๆœ‰ใฎใƒžใƒ‹ใƒ•ใ‚งใ‚นใƒˆ๏ผˆmta.yaml๏ผ‰ใ‚„ใƒขใ‚ธใƒฅใƒผใƒซๆง‹ๆˆใ‚’็ด ๆ—ฉใๆ•ดๅ‚™ใงใใพใ™ใ€‚ย BAS ใง Create Dev Space ใ‚’ใ‚ฏใƒชใƒƒใ‚ฏใ—ใ€ใƒ†ใƒณใƒ—ใƒฌใƒผใƒˆใซ SAP Fiori ใ‚’้ธๆŠžใ—ใพใ™ใ€‚ไฝตใ›ใฆๅ‰ๅ›žใซไฝœๆˆใ—ใŸ Python API ใ‚’ใƒญใƒผใ‚ซใƒซใง่ตทๅ‹•ใ—ใŸใ„ใฎใงใ€Additional SAP Extensions ใ‹ใ‚‰ Python Tools ใ‚’้ธๆŠžใ—ใพใ™ใ€‚ๅทฆไธŠใฎใƒใƒณใƒใƒผใ‚ฌใƒผใƒกใƒ‹ใƒฅใƒผ โ†’ File > New Project From Template ใ‚’้ธๆŠžใ—ใพใ™ใ€‚Template Selection ใฏ Basic ใ‚’้ธๆŠžใ—ใพใ™ใ€‚Data Source and Service Selection ใงใฏใ€UI ใซ่กจ็คบใ™ใ‚‹ใƒ‡ใƒผใ‚ฟใฏ Python API ใ‹ใ‚‰ๅ–ๅพ—ใ™ใ‚‹ใŸใ‚ None ใ‚’้ธๆŠžใ—ใพใ™ใ€‚Entity Selection ใง Entity name ใซ ChatEntity ใจๅ…ฅๅŠ›ใ—ใพใ™ใ€‚Project Attributes ใ‚’ไปฅไธ‹ใฎใ‚ˆใ†ใซ่จญๅฎšใ—ใ€Add deployment configuration ใ‚’ Yes ใซใ—ใพใ™ใ€‚้ …็›ฎๅ€คModule Namemy-ai-agent-uiApplication TitleMy AI ChatDescriptionChat UI for AI AgentDeployment Configuration ใงใฏๆฌกใฎใ‚ˆใ†ใซ่จญๅฎšใ—ใพใ™ใ€‚้ …็›ฎๅ€คTargetCloud FoundryDestinationNoneAdd Router ModuleAdd Application to Managed Application Routerใพใ‚Œใซ Target ใ‚’ CF ใซใ—ใฆใ‚‚ Router Module ใฎใ‚ชใƒ—ใ‚ทใƒงใƒณใŒ่กจ็คบใ•ใ‚Œใชใ„ใ“ใจใŒใ‚ใ‚Šใพใ™ใ€‚ใใฎๅ ดๅˆใฏใ€ไธ€ๅบฆ Target ใ‚’ ABAP ใซๅˆ‡ใ‚Šๆ›ฟใˆใฆใ‹ใ‚‰ Cloud Foundry ใซๆˆปใ™ใจๆญฃใ—ใ่กจ็คบใ•ใ‚Œใพใ™ใ€‚ย ๆœ€ๅพŒใซใ€Python API ใฎ้…็ฝฎใจๅ‹•ไฝœ็ขบ่ชใ‚’ๅฎŸๆ–ฝใ—ใพใ™ใ€‚ๆง‹ๆˆใฏๆฌกใฎใ‚ˆใ†ใซใชใ‚Šใพใ™ใ€‚# ใƒ•ใ‚ฉใƒซใƒ€ๆง‹ๆˆ
PROJECTS
โ”œโ”€โ”€ my-ai-agent-ui/
โ”œโ”€โ”€ my-ai-agent-api/
โ”œโ”€โ”€ main.py
โ”œโ”€โ”€ requirements.txt
โ””โ”€โ”€ .envmy-ai-agent-apiย ้…ไธ‹ใงไปฎๆƒณ็’ฐๅขƒใ‚’ไฝœๆˆใƒปๆœ‰ๅŠนๅŒ–ใ—ใ€ไปฅไธ‹ใฎใ‚ณใƒžใƒณใƒ‰ใงใƒญใƒผใ‚ซใƒซใ‚ตใƒผใƒใŒ่ตทๅ‹•ใ™ใ‚‹ใ‹็ขบ่ชใ—ใพใ™ใ€‚gunicorn -w 1 -k uvicorn.workers.UvicornWorker main:app –bind 0.0.0.0:${PORT:-8000}ใ“ใ‚Œใงใ€Fioriใ‚ขใƒ—ใƒชใ‚ฑใƒผใ‚ทใƒงใƒณใฎๆง‹็ฏ‰ๆบ–ๅ‚™ใŒๆ•ดใ„ใพใ—ใŸ๏ผย 4 | Fiori ใ‚ขใƒ—ใƒชใ‚ฑใƒผใ‚ทใƒงใƒณใฎๆ”น่‰ฏ็”Ÿๆˆใ•ใ‚ŒใŸ Fiori ใƒ—ใƒญใ‚ธใ‚งใ‚ฏใƒˆใ‚’ใƒ™ใƒผใ‚นใซใ€ใƒใƒฃใƒƒใƒˆ UI ใŒ AI Agent ใจ้€šไฟกใงใใ‚‹ใ‚ˆใ†ใซใ‚ณใƒผใƒ‰ใ‚’่ชฟๆ•ดใ—ใฆใ„ใใพใ—ใ‚‡ใ†ใ€‚ใพใšใ‚ณใƒณใƒˆใƒญใƒผใƒฉใƒผ๏ผˆmy-ai-agent-ui/webapp/controller/ChatEntity.controller.js๏ผ‰ใ‚’ไปฅไธ‹ใฎใ‚ˆใ†ใซๆ›ธใใพใ™ใ€‚sap.ui.define([
“sap/ui/core/mvc/Controller”,
“sap/ui/model/json/JSONModel”,
“sap/m/MessageToast”,
“sap/ui/core/BusyIndicator”
], function (Controller, JSONModel, MessageToast, BusyIndicator) {
“use strict”;

// ็’ฐๅขƒใซใ‚ˆใฃใฆใ‚จใƒณใƒ‰ใƒใ‚คใƒณใƒˆใ‚’ๅˆ‡ใ‚Šๆ›ฟใˆ
const ENDPOINT = (
[“localhost”, “applicationstudio”].some(h => window.location.hostname.includes(h)) ||
window.location.port === “8080”
) ? “” : “https://my-ai-agent-api-relaxed-raven-ie.cfapps.us10-001.hana.ondemand.com”;

return Controller.extend(“myaiagentui.controller.ChatEntity”, {
/** ๅˆๆœŸๅŒ– */
onInit() {
this.getView().setModel(new JSONModel({
busy: false,
txtInput: “”,
uploadedFiles: [],
messages: [{
role: “assistant”,
content: “ใ“ใ‚“ใซใกใฏ๏ผ็งใฏใƒใƒฃใƒƒใƒˆใƒœใƒƒใƒˆใงใ™ใ€‚ไฝ•ใ‹ใŠๆ‰‹ไผใ„ใงใใ‚‹ใ“ใจใฏใ‚ใ‚Šใพใ™ใ‹๏ผŸ”,
hasThinkingProcess: false
}]
}), “ui”);

this.byId(“fileUploader”)?.setUploadUrl(`${ENDPOINT}/agent/upload`);
},

/** ใƒใƒฃใƒƒใƒˆๅฑฅๆญดใ‚’ๅˆๆœŸๅŒ– */
onClearChatPress() {
this.getView().getModel(“ui”).setProperty(“/messages”, [{
role: “assistant”,
content: “ใ“ใ‚“ใซใกใฏ๏ผ็งใฏใƒใƒฃใƒƒใƒˆใƒœใƒƒใƒˆใงใ™ใ€‚ไฝ•ใ‹ใŠๆ‰‹ไผใ„ใงใใ‚‹ใ“ใจใฏใ‚ใ‚Šใพใ™ใ‹๏ผŸ”,
hasThinkingProcess: false
}]);
MessageToast.show(“ใƒใƒฃใƒƒใƒˆใ‚’ใ‚ฏใƒชใ‚ขใ—ใพใ—ใŸ”);
},

/** ใƒกใƒƒใ‚ปใƒผใ‚ธ้€ไฟก */
async onBtnChatbotSendPress(){
const ui=this.getView().getModel(“ui”);
const input=(ui.getProperty(“/txtInput”)||””).trim();
if(!input) return;
const msgs=ui.getProperty(“/messages”);
msgs.push({role:”user”, content:input});
ui.setProperty(“/messages”, msgs);
ui.setProperty(“/txtInput”, “”);
ui.setProperty(“/busy”, true);
try{
const {output, intermediate_steps=[]}=await this._apiChatCompletion(input);
msgs.push({role:”assistant”, content:output, hasThinkingProcess:Boolean(intermediate_steps.length), thinkingProcess:intermediate_steps.map((s,i)=>({…s, stepIndex:i+1, observationTruncated:s.observation?.slice(0,100)+(s.observation?.length>100?”…”:””), observationFull:s.observation, isObservationExpanded:false, hasLongObservation:(s.observation?.length||0)>100})), isExpanded:false});
ui.setProperty(“/messages”, msgs);
this._scrollToBottom();
}catch(err){
console.error(err);
MessageToast.show(`ใ‚จใƒฉใƒผ: ${err.message}`);
}finally{
ui.setProperty(“/busy”, false);
}
},

/** ๆ€่€ƒใƒ—ใƒญใ‚ปใ‚น่กจ็คบๅˆ‡ๆ›ฟ */
onToggleThinkingProcess(oEvent) {
const ctx = oEvent.getSource()?.getBindingContext(“ui”);
if (ctx) this._toggleFlag(ctx, “isExpanded”);
},

/** Observation ่กจ็คบๅˆ‡ๆ›ฟ */
onToggleObservation(oEvent) {
const ctx = oEvent.getSource()?.getBindingContext(“ui”);
if (ctx) this._toggleFlag(ctx, “isObservationExpanded”);
},

/** ไปปๆ„ใƒ•ใƒฉใ‚ฐใ‚’ๅ่ปข */
_toggleFlag(ctx, flag) {
const ui = this.getView().getModel(“ui”);
const path = ctx.getPath();
ui.setProperty(`${path}/${flag}`, !ui.getProperty(`${path}/${flag}`));
},

/** ใƒใƒฃใƒƒใƒˆใ‚’ๆœ€ไธ‹้ƒจใธใ‚นใ‚ฏใƒญใƒผใƒซ */
_scrollToBottom() {
setTimeout(() => {
const sc = this.byId(“chatScrollContainer”);
const items = sc?.getContent()[0].getItems();
if (items?.length) sc.scrollToElement(items[items.length – 1]);
}, 100);
},

/** AI ใƒใƒฃใƒƒใƒˆ API ๅ‘ผใณๅ‡บใ— */
async _apiChatCompletion(query) {
const res = await fetch(`${ENDPOINT}/agent/chat`, {
method: “POST”,
headers: { “Content-Type”: “application/json” },
body: JSON.stringify({ query })
});
if (!res.ok) throw new Error(`ใ‚ตใƒผใƒใƒผใ‚จใƒฉใƒผ (${res.status}): ${await res.text()}`);
return res.json();
}
});
});ย ๆฌกใซ View๏ผˆmy-ai-agent-ui/webapp/view/ChatEntity.view.xml๏ผ‰ใ‚’ไปฅไธ‹ใฎใ‚ˆใ†ใซๅค‰ๆ›ดใ—ใพใ™ใ€‚<mvc:View controllerName=”myaiagentui.controller.ChatEntity”
xmlns:core=”sap.ui.core”
xmlns:mvc=”sap.ui.core.mvc”
xmlns:f=”sap.f”
xmlns:u=”sap.ui.unified”
displayBlock=”true”
xmlns=”sap.m”>

<f:DynamicPage id=”mainDynamicPage” stickySubheaderProvider=”iconTabBar” class=”sapUiNoContentPadding”>
<f:title>
<f:DynamicPageTitle id=”mainDynamicTitle”>
<f:heading>
<Title id=”pageTitle” text=”AI ใƒใƒฃใƒƒใƒˆ with Fiori”/>
</f:heading>
<f:actions>
<Button id=”clearChatButton”
text=”ใƒใƒฃใƒƒใƒˆใ‚’ใ‚ฏใƒชใ‚ข”
icon=”sap-icon://refresh”
type=”Emphasized”
press=”onClearChatPress”/>
</f:actions>
</f:DynamicPageTitle>
</f:title>

<f:content>
<!– ใƒใƒฃใƒƒใƒˆ UI ใƒกใ‚คใƒณใ‚จใƒชใ‚ข –>
<VBox id=”chatMainAreaBox” class=”sapUiNoContentPadding chatMainArea”>
<IconTabBar id=”iconTabBar”
class=”sapUiNoContentPadding”
stretchContentHeight=”true”
expanded=”true”
expandable=”false”>

<items>
<IconTabFilter id=”chatbotTab” text=”ใƒใƒฃใƒƒใƒˆใƒœใƒƒใƒˆ” class=”sapUiNoContentPadding”>
<VBox id=”chatContainerBox” class=”chatContainer sapUiNoContentPadding”>
<!– ใƒใƒฃใƒƒใƒˆใƒกใƒƒใ‚ปใƒผใ‚ธใ‚จใƒชใ‚ข –>
<ScrollContainer id=”chatScrollContainer”
height=”100%”
width=”100%”
vertical=”true”
focusable=”true”
class=”chatScrollContainer”>
<VBox id=”chatMessagesBox” items=”{ui>/messages}” class=”chatMessagesContainer”>
<VBox id=”messageItemBox”>
<!– assistant ใ‚ณใƒกใƒณใƒˆ –>
<VBox id=”assistantMessageVBox” visible=”{= ${ui>role} === ‘assistant’}”
class=”chatMessageWrapper”>
<HBox id=”assistantMessageHBox” direction=”Row” class=”chatMessageRow”>
<HBox id=”assistantMessageHBox1″ backgroundDesign=”Solid”
class=”chatBubbleAssistant”>
<core:Icon id=”assistantMessageIcon” decorative=”true”
src=”sap-icon://ai”
class=”chatIcon”/>
<FormattedText id=”assistantMessageFormattedText” htmlText=”{ui>content}”/>
</HBox>
</HBox>

<!– ๆ€่€ƒใƒ—ใƒญใ‚ปใ‚นใ‚ขใ‚ณใƒผใƒ‡ใ‚ฃใ‚ชใƒณ –>
<HBox id=”thinkingToggleBox” visible=”{ui>hasThinkingProcess}”
class=”thinkingProcessContainer”>
<Button id=”thinkingToggleButton” icon=”{= ${ui>isExpanded} ? ‘sap-icon://collapse’ : ‘sap-icon://expand’ }”
text=”ๆ€่€ƒใƒ—ใƒญใ‚ปใ‚นใ‚’{= ${ui>isExpanded} ? ‘้š ใ™’ : ‘่กจ็คบ’ }”
type=”Transparent”
press=”onToggleThinkingProcess”
class=”thinkingProcessToggle”/>
</HBox>

<VBox id=”thinkingVBox” visible=”{= ${ui>isExpanded} &amp;&amp; ${ui>hasThinkingProcess} }”
class=”thinkingProcessContent”>
<VBox id=”thinkingVBox1″ items=”{ui>thinkingProcess}”>
<VBox id=”thinkingStepBox” class=”thinkingStepBox”>
<Title id=”thinkingStepTitle”
text=”{= ‘Step ‘ + ${ui>stepIndex} }”
level=”H5″
class=”thinkingStepTitle”/>
<VBox id=”thinkingStepContent” class=”thinkingStepContent”>
<HBox id=”thinkingStepThought” class=”thinkingStepItem”>
<Label id=”ThoughtLabel” text=”Thought:” design=”Bold” class=”thinkingLabel”/>
<Text id=”ThoughtText” text=”{ui>thought}” wrapping=”true” class=”thinkingText”/>
</HBox>
<HBox id=”thinkingStepAction” class=”thinkingStepItem”>
<Label id=”ActionLabel” text=”Action:” design=”Bold” class=”thinkingLabel”/>
<Text id=”ActionText” text=”{ui>action}” class=”thinkingText”/>
</HBox>
<HBox id=”thinkingStepInput” class=”thinkingStepItem”>
<Label id=”InputLabel” text=”Input:” design=”Bold” class=”thinkingLabel”/>
<Text id=”InputText” text=”{ui>action_input}” class=”thinkingText”/>
</HBox>
<HBox id=”thinkingStepObservation” class=”thinkingStepItem”>
<Label id=”ObservationLabel” text=”Observation:” design=”Bold” class=”thinkingLabel”/>
<VBox id=”ObservationVBox” class=”thinkingText”>
<Text id=”ObservationText” text=”{= ${ui>isObservationExpanded} ? ${ui>observationFull} : ${ui>observationTruncated} }”
wrapping=”true”/>
<Link id=”ObservationLink”
text=”{= ${ui>isObservationExpanded} ? ‘ๆŠ˜ใ‚ŠใŸใŸใ‚€’ : ‘็ถšใใ‚’่ชญใ‚€’ }”
visible=”{ui>hasLongObservation}”
press=”onToggleObservation”
class=”observationToggleLink”/>
</VBox>
</HBox>
</VBox>
</VBox>
</VBox>
</VBox>
</VBox>

<!– user ใ‚ณใƒกใƒณใƒˆ –>
<HBox id=”UserComment”
visible=”{= ${ui>role} === ‘user’}”
direction=”RowReverse”
class=”chatMessageRow”>
<HBox id=”UserCommentBox”
direction=”RowReverse”
backgroundDesign=”Solid”
class=”chatBubbleUser sapThemeBrand-asBackgroundColor”>
<core:Icon id=”UserCommentIcon”
decorative=”true”
src=”sap-icon://customer”
class=”chatIcon sapThemeTextInverted”/>
<Text id=”UserCommentText” class=”sapThemeTextInverted” text=”{ui>content}”/>
</HBox>
</HBox>
</VBox>
</VBox>
</ScrollContainer>

<!– ๅ…ฅๅŠ›ๆฌ„๏ผ‹้€ไฟกใƒœใ‚ฟใƒณ –>
<HBox id=”chatInputAreaBox” class=”chatInputArea”>
<TextArea id=”chatInput” value=”{ui>/txtInput}”
width=”100%”
growing=”true”
placeholder=”ใƒกใƒƒใ‚ปใƒผใ‚ธใ‚’ๅ…ฅๅŠ›ใ—ใฆใใ ใ•ใ„…”
editable=”{= !${ui>/busy}}”
busyIndicatorDelay=”0″
class=”chatTextArea”>
<layoutData>
<FlexItemData id=”InputData” growFactor=”1″/>
</layoutData>
</TextArea>

<Button id=”sendButton” class=”chatSendButton”
type=”Emphasized”
icon=”sap-icon://paper-plane”
text=”้€ไฟก”
press=”onBtnChatbotSendPress”
busy=”{ui>/busy}”
busyIndicatorDelay=”0″/>
</HBox>
</VBox>
</IconTabFilter>
</items>
</IconTabBar>
</VBox>
</f:content>
</f:DynamicPage>
</mvc:View>ย ใ•ใ‚‰ใซใ€UI ใฎ่ฉณ็ดฐใชใƒ‡ใ‚ถใ‚คใƒณ่จญๅฎšใจใ—ใฆใ€ใ‚นใ‚ฟใ‚คใƒซ๏ผˆmy-ai-agent-ui/webapp/css/style.css๏ผ‰ใ‚’่จญๅฎšใ—ใพใ™ใ€‚/* ใƒใƒฃใƒƒใƒˆใƒกใ‚คใƒณใ‚จใƒชใ‚ข */
.chatMainArea {
height: 100vh;
min-height: 300px;
}

/* ใƒใƒฃใƒƒใƒˆใ‚ณใƒณใƒ†ใƒŠ */
.chatContainer {
display: flex;
flex-direction: column;
height: calc(90vh – 100px);
padding: 0;
}

/* ใƒใƒฃใƒƒใƒˆใ‚นใ‚ฏใƒญใƒผใƒซใ‚ณใƒณใƒ†ใƒŠ */
.chatScrollContainer {
flex: 1;
min-height: 300px;
background-color: #f8f9fa;
border: none;
border-radius: 0;
padding: 0;
margin: 0;
}

/* ใƒใƒฃใƒƒใƒˆใƒกใƒƒใ‚ปใƒผใ‚ธใ‚ณใƒณใƒ†ใƒŠ */
.chatMessagesContainer {
padding: 1rem;
}

/* ใƒใƒฃใƒƒใƒˆใƒกใƒƒใ‚ปใƒผใ‚ธ่กŒ */
.chatMessageRow {
width: 100%;
margin-bottom: 0.5rem;
}

/* ใƒใƒฃใƒƒใƒˆใƒกใƒƒใ‚ปใƒผใ‚ธใƒฉใƒƒใƒ‘ใƒผ */
.chatMessageWrapper {
margin-bottom: 1rem;
}

/* ใƒใƒฃใƒƒใƒˆใ‚ณใƒกใƒณใƒˆ – ใ‚ขใ‚ทใ‚นใ‚ฟใƒณใƒˆ */
.chatBubbleAssistant {
max-width: 70%;
min-width: 100px;
background-color: #ffffff;
border: 1px solid #d0d7de;
border-radius: 18px;
padding: 12px 16px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
align-items: flex-start;
}

.chatBubbleAssistant .sapMFormattedText {
word-wrap: break-word;
line-height: 1.4;
}

/* ใƒใƒฃใƒƒใƒˆใ‚ณใƒกใƒณใƒˆ – ใƒฆใƒผใ‚ถใƒผ */
.chatBubbleUser {
max-width: 70%;
min-width: 100px;
border-radius: 18px;
padding: 12px 16px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
align-items: flex-start;
}

.chatBubbleUser .sapMText {
word-wrap: break-word;
line-height: 1.4;
}

/* ๆ€่€ƒใƒ—ใƒญใ‚ปใ‚นใ‚ณใƒณใƒ†ใƒŠ */
.thinkingProcessContainer {
margin-top: 0.5rem;
margin-left: 2.5rem;
}

/* ๆ€่€ƒใƒ—ใƒญใ‚ปใ‚นใƒˆใ‚ฐใƒซใƒœใ‚ฟใƒณ */
.thinkingProcessToggle {
font-size: 0.875rem;
color: #0073e6;
}

.thinkingProcessToggle:hover {
background-color: rgba(0, 115, 230, 0.08) !important;
}

/* ๆ€่€ƒใƒ—ใƒญใ‚ปใ‚นใ‚ณใƒณใƒ†ใƒณใƒ„ */
.thinkingProcessContent {
margin-left: 2.5rem;
margin-top: 0.5rem;
max-width: 70%;
}

/* ๆ€่€ƒใ‚นใƒ†ใƒƒใƒ—ใƒœใƒƒใ‚ฏใ‚น */
.thinkingStepBox {
margin-bottom: 1.5rem;
padding: 1rem;
background-color: #f5f7fa;
border: 1px solid #e4e7ea;
border-radius: 8px;
}

.thinkingStepBox:last-child {
margin-bottom: 0;
}

/* ๆ€่€ƒใ‚นใƒ†ใƒƒใƒ—ใ‚ฟใ‚คใƒˆใƒซ */
.thinkingStepTitle {
color: #0a6ed1;
margin-bottom: 0.75rem;
font-size: 1rem;
}

/* ๆ€่€ƒใ‚นใƒ†ใƒƒใƒ—ใ‚ณใƒณใƒ†ใƒณใƒ„ */
.thinkingStepContent {
padding-left: 0.5rem;
}

/* ๆ€่€ƒใ‚นใƒ†ใƒƒใƒ—้ …็›ฎ */
.thinkingStepItem {
margin-bottom: 0.5rem;
align-items: flex-start;
}

.thinkingStepItem:last-child {
margin-bottom: 0;
}

/* ๆ€่€ƒใƒฉใƒ™ใƒซ */
.thinkingLabel {
min-width: 100px;
margin-right: 1rem;
color: #32363a;
font-size: 0.875rem;
}

/* ๆ€่€ƒใƒ†ใ‚ญใ‚นใƒˆ */
.thinkingText {
flex: 1;
font-size: 0.875rem;
color: #515456;
line-height: 1.5;
}

/* Observation็ถšใใ‚’่ชญใ‚€ใƒชใƒณใ‚ฏ */
.observationToggleLink {
margin-top: 0.25rem;
font-size: 0.875rem;
}

/* ๅ…ฅๅŠ›ใ‚จใƒชใ‚ข */
.chatInputArea {
background-color: #ffffff;
padding: 1rem;
border-top: 1px solid #e4e7ea;
align-items: flex-end;
flex-shrink: 0;
}

/* ใƒ†ใ‚ญใ‚นใƒˆใ‚จใƒชใ‚ขใฎใ‚นใ‚ฟใ‚คใƒซ่ชฟๆ•ด */
.chatTextArea .sapMTextAreaInner {
border: 2px solid #e4e7ea;
background-color: #ffffff;
}

.chatTextArea .sapMTextAreaInner:focus-within {
border-color: #0073e6;
}

/* ้€ไฟกใƒœใ‚ฟใƒณ */
.chatSendButton {
min-height: 40px;
border-radius: 20px;
margin-left: 0.5rem;
}

/* ใ‚ขใ‚คใ‚ณใƒณ่ชฟๆ•ด */
.chatIcon {
font-size: 1.2rem;
margin-top: 2px;
}

.chatBubbleAssistant .chatIcon {
margin-right: 0.5rem;
}

.chatBubbleUser .chatIcon {
margin-left: 0.5rem;
}

/* IconTabBar ใฎใƒ‘ใƒ‡ใ‚ฃใƒณใ‚ฐๅ‰Š้™ค */
.sapMITB {
padding: 0;
}

.sapMITBContent {
padding: 0;
background-color: transparent;
}

/* ใƒฌใ‚นใƒใƒณใ‚ทใƒ–ๅฏพๅฟœ */
@media (max-width: 768px) {
.chatBubbleAssistant,
.chatBubbleUser {
max-width: 85%;
}

.chatContainer {
height: calc(100vh – 80px);
}

.chatScrollContainer {
min-height: 300px;
}

.chatSendButton .sapMBtnInner .sapMBtnContent {
font-size: 0;
}

.chatSendButton .sapMBtnIcon {
margin-right: 0;
}

.thinkingProcessContent {
max-width: 85%;
}

.thinkingLabel {
min-width: 80px;
margin-right: 0.5rem;
}
}ย ใƒใƒฃใƒƒใƒˆ UI ใ‹ใ‚‰ Python API ใธใƒชใ‚ฏใ‚จใ‚นใƒˆใ‚’่ปข้€ใ™ใ‚‹ใŸใ‚ใ€ใƒ—ใƒญใ‚ญใ‚ทใƒŸใƒ‰ใƒซใ‚ฆใ‚งใ‚ขใ‚’่ฟฝๅŠ ใ—ใพใ™ใ€‚ใพใšไพๅญ˜้–ขไฟ‚ใ‚’ใ‚คใƒณใ‚นใƒˆใƒผใƒซใ—ใพใ—ใ‚‡ใ†ใ€‚npm install ui5-middleware-simpleproxy –save-devย ใใ—ใฆ my-ai-agent-ui/package.json ใซๆฌกใฎ่จญๅฎšใ‚’่ฟฝ่จ˜ใ—ใฆใใ ใ•ใ„ใ€‚…
“main”: “webapp/index.html”,
“devDependencies”: {
(็œ็•ฅ)
},
/* ใ“ใ“ใ‹ใ‚‰
“ui5”: {
“dependencies”: [
“ui5-middleware-simpleproxy”
ใ€€ใ€€]
},
ใ€€ใ“ใ“ใพใง */
“scripts”: {
….ใ“ใ‚Œใงใƒใƒฃใƒƒใƒˆ UI ใ‹ใ‚‰ใฎ /api ใƒชใ‚ฏใ‚จใ‚นใƒˆใŒใƒญใƒผใ‚ซใƒซใฎ Python API ใธ่ปข้€ใ•ใ‚Œใ‚‹ใ‚ˆใ†ใซใชใ‚Šใพใ™ใ€‚ย 5 | Fiori ใ‚ขใƒ—ใƒชใ‚ฑใƒผใ‚ทใƒงใƒณใฎใƒ†ใ‚นใƒˆใƒญใƒผใ‚ซใƒซ็’ฐๅขƒใงใ‚ขใƒ—ใƒชใจ API ใ‚’ๅŒๆ™‚ใซ่ตทๅ‹•ใ—ใ€ๅฎŸ้š›ใซใƒใƒฃใƒƒใƒˆๆฉŸ่ƒฝใŒๅ‹•ไฝœใ™ใ‚‹ใ‹็ขบ่ชใ—ใพใ—ใ‚‡ใ†ใ€‚ใพใšใ€my-ai-agent-api ใƒ‡ใ‚ฃใƒฌใ‚ฏใƒˆใƒชใงไปฅไธ‹ใฎใ‚ณใƒžใƒณใƒ‰ใ‚’ๅฎŸ่กŒใ—ใฆ Python API ใ‚’่ตทๅ‹•ใ—ใพใ™ใ€‚gunicorn -w 1 -k uvicorn.workers.UvicornWorker main:app –bind 0.0.0.0:${PORT:-8000}ใพใŸใ€ๅˆฅใฎใ‚ฟใƒผใƒŸใƒŠใƒซใ‚’้–‹ใใ€my-ai-agent-ui ใฎใƒซใƒผใƒˆใงๆฌกใฎใ‚ณใƒžใƒณใƒ‰ใ‚’ๅฎŸ่กŒใ—ใพใ™ใ€‚npm startย ใƒญใ‚ฐใซ่กจ็คบใ•ใ‚ŒใŸ URL๏ผˆ้€šๅธธใฏ http://localhost:8080๏ผ‰ใ‚’ใƒ–ใƒฉใ‚ฆใ‚ถใง้–‹ใใ€ใƒใƒฃใƒƒใƒˆๅ…ฅๅŠ›ๆฌ„ใซใƒกใƒƒใ‚ปใƒผใ‚ธใ‚’้€ใฃใฆใฟใฆใใ ใ•ใ„ใ€‚AI ใ‚จใƒผใ‚ธใ‚งใƒณใƒˆใ‹ใ‚‰่ฟ”็ญ”ใŒ่กจ็คบใ•ใ‚Œใ‚ŒใฐๆˆๅŠŸใงใ™๏ผย ใƒใƒฃใƒƒใƒˆ็”ป้ขใฎ ใƒใƒฃใƒƒใƒˆใ‚’ใ‚ฏใƒชใ‚ข ใƒœใ‚ฟใƒณใ‚’ใ‚ฏใƒชใƒƒใ‚ฏใ™ใ‚‹ใจๅฑฅๆญดใŒใƒชใ‚ปใƒƒใƒˆใ•ใ‚Œใ€ใ€Œใƒใƒฃใƒƒใƒˆใ‚’ใ‚ฏใƒชใ‚ขใ—ใพใ—ใŸใ€ใจ็ขบ่ชใงใใพใ™ใ€‚ๆ–ฐใ—ใ„ไผš่ฉฑใ‚’ๅ•้กŒใชใๅง‹ใ‚ใ‚‰ใ‚Œใ‚‹ใ‹็ขบ่ชใ—ใพใ—ใ‚‡ใ†๏ผย 6 | ใƒใƒฃใƒฌใƒณใ‚ธ โ€“ ใƒ†ใ‚ญใ‚นใƒˆใƒ•ใ‚กใ‚คใƒซใ‚ขใƒƒใƒ—ใƒญใƒผใƒ‰ใƒœใ‚ฟใƒณใ‚’่ฟฝๅŠ ใ—ใ‚ˆใ†ใƒ†ใ‚ญใ‚นใƒˆใ‚’่ฟฝๅŠ ใงใ‚ขใƒƒใƒ—ใƒญใƒผใƒ‰ใ—ใฆใ€่ฟฝๅŠ ใ—ใŸใƒ‰ใ‚ญใƒฅใƒกใƒณใƒˆใ‚’่ธใพใˆใŸ AI Agent ใจใฎใ‚„ใ‚Šใจใ‚Šใ‚’ๅฎŸ็พใ—ใฆ่ฆ‹ใพใ—ใ‚‡ใ†ใ€‚ใชใŠใ€ใ“ใฎใƒใƒฃใƒฌใƒณใ‚ธใฏๅ‰ๅ›žใฎใƒใƒฃใƒฌใƒณใ‚ธใ‚’ๅฎŸๆ–ฝใ—ใฆใ„ใชใ„ใจๅฎŸ่ฃ…ใงใใชใ„ใฎใงๆณจๆ„ใ—ใพใ—ใ‚‡ใ†ใ€‚ใƒใƒฃใƒƒใƒˆ UI ใฎใƒ˜ใƒƒใƒ€ใƒผใซ .txt ใƒ•ใ‚กใ‚คใƒซๅฐ‚็”จใฎใ‚ขใƒƒใƒ—ใƒญใƒผใƒ‰ใƒœใ‚ฟใƒณ ใ‚’่ฟฝๅŠ ใ—ใ€้ธๆŠžใ—ใŸใƒ•ใ‚กใ‚คใƒซใ‚’ Python API ใธ้€ไฟกใ—ใ€ใ‚ตใƒผใƒๅดใงใƒ•ใ‚กใ‚คใƒซใ‚’ใƒใƒฃใƒณใ‚ฏๅŒ–ใ—ใฆไฟๅญ˜ใ™ใ‚‹ใ‚ˆใ†ใซใ—ใพใ™ใ€‚้€”ไธญ็ตŒ้Žใ‚’ใ‚ใ‹ใ‚Šใ‚„ใ™ใใ™ใ‚‹ใŸใ‚ใ€ใ‚ขใƒƒใƒ—ใƒญใƒผใƒ‰ไธญใฏ BusyIndicator ใง็”ป้ขใ‚’ใƒ–ใƒญใƒƒใ‚ฏใ™ใ‚‹ใ‚ˆใ†ใซใ—ใพใ—ใ‚‡ใ†ใ€‚ย ChatEntity.controller.js ใงๆฌกใฎ 3 ็‚นใ‚’่ฟฝๅŠ ใ—ใพใ™๏ผˆใƒ‘ใ‚นใ‚„ๅๅ‰็ฉบ้–“ใฏ็’ฐๅขƒใซๅˆใ‚ใ›ใฆใใ ใ•ใ„๏ผ‰:ๅ†’้ ญใฎ sap/ui/core/BusyIndicator ใ‚คใƒณใƒใƒผใƒˆonFileChange ใƒใƒณใƒ‰ใƒฉ โ€“ ใƒ•ใ‚กใ‚คใƒซ็จฎๅˆฅใƒใ‚งใƒƒใ‚ฏ & ใ‚ขใƒƒใƒ—ใƒญใƒผใƒ‰ๅ‘ผใณๅ‡บใ—_uploadFile ใƒกใ‚ฝใƒƒใƒ‰ โ€“ fetch + FormData ใง API /agent/upload ใธ POST/** ใƒ•ใ‚กใ‚คใƒซ้ธๆŠžๆ™‚ */
async onFileChange(oEvent) {
const oFileUploader = oEvent.getSource();
const file = (oEvent.getParameter(“files”) || [])[0];
if (!file) return;
if (!file.name.endsWith(“.txt”)) {
MessageToast.show(“txtๅฝขๅผใฎใƒ•ใ‚กใ‚คใƒซใฎใฟใ‚ขใƒƒใƒ—ใƒญใƒผใƒ‰ๅฏ่ƒฝใงใ™ใ€‚”);
oFileUploader.clear();
return;
}
await this._uploadFile(file);
oFileUploader.clear();
},

/** ใƒ•ใ‚กใ‚คใƒซใ‚ขใƒƒใƒ—ใƒญใƒผใƒ‰ */
async _uploadFile(file) {
const ui = this.getView().getModel(“ui”);
ui.setProperty(“/busy”, true);
BusyIndicator.show(0);
try {
const fd = new FormData();
fd.append(“file”, file);
const res = await fetch(`${ENDPOINT}/agent/upload`, {
method: “POST”,
body: fd
});
if (!res.ok) throw new Error(await res.text());
const { filename, chunks_created } = await res.json();
const files = ui.getProperty(“/uploadedFiles”) || [];
ui.setProperty(“/uploadedFiles”, […files, {
name: filename,
chunks: chunks_created,
uploadedAt: new Date()
}]);
MessageToast.show(`”${filename}” ใ‚’ใ‚ขใƒƒใƒ—ใƒญใƒผใƒ‰ใ—ใพใ—ใŸ๏ผˆ${chunks_created} ใƒใƒฃใƒณใ‚ฏไฝœๆˆ๏ผ‰`);
} catch (err) {
console.error(err);
MessageToast.show(`ใ‚ขใƒƒใƒ—ใƒญใƒผใƒ‰ใ‚จใƒฉใƒผ: ${err.message}`);
} finally {
ui.setProperty(“/busy”, false);
BusyIndicator.hide();
}
}ย ๆฌกใซใ€ใƒ˜ใƒƒใƒ€ใƒผใธ FileUploader ใจ Clear ใƒœใ‚ฟใƒณใ‚’ไธฆในใพใ™ใ€‚FileUploader ใฏ buttonOnly=”true” ใซใ—ใฆใ€ใ‚ขใ‚คใ‚ณใƒณใƒœใ‚ฟใƒณใซใ™ใ‚‹ใฎใŒใ‚ณใƒ„ใงใ™ใ€‚<f:actions>
<u:FileUploader id=”fileUploader”
name=”file”
placeholder=”ใƒ•ใ‚กใ‚คใƒซใ‚’้ธๆŠž…”
fileType=”txt”
change=”onFileChange”
buttonText=””
buttonOnly=”true”
icon=”sap-icon://upload”
class=”fileUploaderButton” />
<Button id=”clearChatButton”
text=”ใƒใƒฃใƒƒใƒˆใ‚’ใ‚ฏใƒชใ‚ข”
icon=”sap-icon://refresh”
type=”Emphasized”
press=”onClearChatPress” />
</f:actions>ย ใ•ใ‚‰ใซใƒ˜ใƒƒใƒ€ใƒผไธ‹้ƒจใซย “ใ‚ขใƒƒใƒ—ใƒญใƒผใƒ‰ๆธˆใƒ•ใ‚กใ‚คใƒซไธ€่ฆง” ใ‚’ๅ‡บใ™ใ“ใจใงใ€ใฉใฎใƒ•ใ‚กใ‚คใƒซใŒไฝ•ใƒใƒฃใƒณใ‚ฏใซๅˆ†ๅ‰ฒใ•ใ‚ŒใŸใ‹ใ‚’่ฆ–่ฆšๅŒ–ใงใใพใ™ใ€‚<HBox id=”fileUploadAreaBox”
visible=”{= ${ui>/uploadedFiles}.length > 0}”>
<Text text=”่ฟฝๅŠ ใงใ‚ขใƒƒใƒ—ใƒญใƒผใƒ‰ใ—ใŸใƒ•ใ‚กใ‚คใƒซ:” />
<HBox items=”{ui>/uploadedFiles}”>
<ObjectStatus text=”{ui>name}” icon=”sap-icon://document-text” />
</HBox>
</HBox>ย ไธŠ็ดšใƒใƒฃใƒฌใƒณใ‚ธ โ€“ ใƒชใ‚ปใƒƒใƒˆใƒœใ‚ฟใƒณใƒ•ใ‚กใ‚คใƒซใ‚’ใ‚ขใƒƒใƒ—ใƒญใƒผใƒ‰ใ—ใฆใ„ใใจใƒ™ใ‚ฏใƒˆใƒซ DB ใŒใฉใ‚“ใฉใ‚“่†จใ‚‰ใฟใพใ™ใ€‚ใƒ†ใ‚นใƒˆ็”จใซ ใƒ‡ใƒผใ‚ฟใƒ™ใƒผใ‚นใ‚’ไธ€ๆ‹ฌใƒชใ‚ปใƒƒใƒˆ ใ™ใ‚‹ใƒœใ‚ฟใƒณใ‚’่ฟฝๅŠ ใ—ใฆใฟใพใ—ใ‚‡ใ†ใ€‚API ๅด: /agent/delete ใ‚’ๅฎŸ่ฃ…๏ผˆใƒ’ใƒณใƒˆ: Part4 ใซใŠใ‘ใ‚‹ DB ใ‚’ๅˆๆœŸๅŒ–ใ™ใ‚‹ใ‚ณใƒผใƒ‰๏ผ‰ใ€‚Controller: onDeletePress ใ‚’ๅฎŸ่ฃ…ใ—ใ€/agent/delete ใ‚’ๅ‘ผใณๅ‡บใ—ใฆ UI ใฎใƒ•ใ‚กใ‚คใƒซไธ€่ฆงใ‚‚็ฉบใซใ™ใ‚‹ใ€‚View: ใƒ˜ใƒƒใƒ€ใƒผใฎใ‚ขใƒƒใƒ—ใƒญใƒผใƒ‰ใƒœใ‚ฟใƒณใจใ‚ฏใƒชใ‚ขใƒœใ‚ฟใƒณใฎ้–“ใซ Delete ใƒœใ‚ฟใƒณใ‚’้…็ฝฎใ€‚ๅฎŸ่ฃ…ใƒ’ใƒณใƒˆUI Model ใฎ /uploadedFiles ใ‚’็ฉบ้…ๅˆ— [] ใซใ™ใ‚‹ใจใ€ใƒ•ใ‚กใ‚คใƒซไธ€่ฆงใ‚จใƒชใ‚ขใŒ่‡ชๅ‹•็š„ใซ้ž่กจ็คบใซใชใ‚Šใพใ™ใ€‚โ€ƒย 7 | ๆฌกๅ›žไบˆๅ‘ŠPart 8 CloudFoundry ใซใƒ‡ใƒ—ใƒญใ‚คPartโ€ฏ8 ใงใฏใ€ๅฎŒๆˆใ—ใŸใ‚ขใƒ—ใƒชใ‚ฑใƒผใ‚ทใƒงใƒณใ‚’ Cloud Foundry ใธใƒ‡ใƒ—ใƒญใ‚คใ—ใพใ™ใ€‚UI ใฏ mta build โ†’ cf deployใ€Python ใƒใƒƒใ‚ฏใ‚จใƒณใƒ‰ใฏ manifest.yaml ใ‚’ไฝฟใฃใฆ cf push โ€• 2 ๆœฌ็ซ‹ใฆใง โ€œ็ง’้€Ÿใƒ‡ใƒ—ใƒญใ‚คโ€ ใซๆŒ‘ๆˆฆใ—ใพใ™ใ€‚ใŠๆฅฝใ—ใฟใซ๏ผย ๅ…่ฒฌไบ‹้ …ๆœฌใƒ–ใƒญใ‚ฐใซ่จ˜่ผ‰ใ•ใ‚ŒใŸ่ฆ‹่งฃใŠใ‚ˆใณๆ„่ฆ‹ใฏใ™ในใฆ็งๅ€‹ไบบใฎใ‚‚ใฎใงใ‚ใ‚Šใ€็งใฎๅ€‹ไบบ็š„ใช็ซ‹ๅ ดใง็™บไฟกใ—ใฆใ„ใพใ™ใ€‚SAP ใฏๆœฌใƒ–ใƒญใ‚ฐใฎๅ†…ๅฎนใซใคใ„ใฆไธ€ๅˆ‡ใฎ่ฒฌไปปใ‚’่ฒ ใ„ใพใ›ใ‚“ใ€‚”}]]ย Read Moreย Technology Blog Posts by SAP articlesย 

#SAPCHANNEL

By ali