ЁЯСЧ Flutter, ADK Go, рдФрд░ Gemini рдХреА рдорджрдж рд╕реЗ рд╡рд░реНрдЪреБрдЕрд▓ рдлрд╝рд┐рдЯрд┐рдВрдЧ рд░реВрдо рдФрд░ рдПрдЖрдИ рд╕реНрдЯрд╛рдЗрд▓рд┐рд╕реНрдЯ рдмрдирд╛рдирд╛

1. рдкрд░рд┐рдЪрдп

рдЖрдкрдХреЛ рдХреНрдпрд╛ рдмрдирд╛рдирд╛ рд╣реИ

рдЗрд╕ рдХреЛрдбрд▓реИрдм рдореЗрдВ, рдЖрдкрдХреЛ рдбреЗрд╡рд▓рдкрд░ рдХреА рднреВрдорд┐рдХрд╛ рдирд┐рднрд╛рдиреА рд╣реЛрдЧреА. рдбреЗрд╡рд▓рдкрд░ рдХреЛ Fashion App рдмрдирд╛рдирд╛ рд╣реИ. рдпрд╣ рдПрдХ рдХрд╛рд▓реНрдкрдирд┐рдХ рдЦреБрджрд░рд╛ рдмреНрд░реИрдВрдб рдХреЗ рд▓рд┐рдП, Flutter рдХрд╛ рд╢реЙрдкрд┐рдВрдЧ рдРрдкреНрд▓рд┐рдХреЗрд╢рди рд╣реИ. рдЖрдкрдХрд╛ рдорд┐рд╢рди: рдПрдЖрдИ рдХреА рдорджрдж рд╕реЗ рдХрд╛рдо рдХрд░рдиреЗ рд╡рд╛рд▓реА рджреЛ рдРрд╕реА рд╕реБрд╡рд┐рдзрд╛рдПрдВ рдЬреЛрдбрд╝реЗрдВ рдЬрд┐рдирд╕реЗ рдСрдирд▓рд╛рдЗрди рд╢реЙрдкрд┐рдВрдЧ рдХрд╛ рдЕрдиреБрднрд╡ рдмреЗрд╣рддрд░ рд╣реЛ.

  1. рд╡рд░реНрдЪреБрдЕрд▓ рдлрд╝рд┐рдЯрд┐рдВрдЧ рд░реВрдо тАФ рдЗрд╕рдореЗрдВ рдХреЛрдИ рд╡реНрдпрдХреНрддрд┐ рдЕрдкрдиреА рдлрд╝реЛрдЯреЛ рдЕрдкрд▓реЛрдб рдХрд░рддрд╛ рд╣реИ. рдЗрд╕рдХреЗ рдмрд╛рдж, рд╡рд╣ рдХрдкрдбрд╝реЛрдВ рдХрд╛ рдХреЛрдИ рдЖрдЗрдЯрдо рдЪреБрдирддрд╛ рд╣реИ. рдлрд┐рд░, рдЙрд╕реЗ рдПрдЖрдИ рд╕реЗ рдЬрдирд░реЗрдЯ рдХреА рдЧрдИ рдРрд╕реА рдЗрдореЗрдЬ рджрд┐рдЦрддреА рд╣реИ рдЬрд┐рд╕рдореЗрдВ рдЙрд╕рдиреЗ рд╡рд╣ рдЖрдЗрдЯрдо рдкрд╣рдирд╛ рд╣реБрдЖ рд╣реИ.
  2. рдПрдЖрдИ рд╕реНрдЯрд╛рдЗрд▓рд┐рд╕реНрдЯ тАФ рдПрдЖрдИ рдПрдЬреЗрдВрдЯ, рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреА рдЬрдЧрд╣, рдЕрд╡рд╕рд░, рдФрд░ рд╕реНрдЯрд╛рдЗрд▓ рдХреА рдкреНрд░рд╛рдердорд┐рдХрддрд╛рдУрдВ рдХреЗ рдЖрдзрд╛рд░ рдкрд░, рдкреВрд░реЗ рдХрдкрдбрд╝реЛрдВ рдХреЗ рд╕реБрдЭрд╛рд╡ рджреЗрддрд╛ рд╣реИ. рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛, рдмрд╛рддрдЪреАрдд рдХреЗ рдЬрд╝рд░рд┐рдП рдЗрди рд╕реБрдЭрд╛рд╡реЛрдВ рдХреЛ рдмреЗрд╣рддрд░ рдмрдирд╛ рд╕рдХрддрд╛ рд╣реИ.

рдЗрд╕рдХрд╛ рдЖрдЗрдбрд┐рдпрд╛ рдЖрд╕рд╛рди рд╣реИ: рдЬрдм рд▓реЛрдЧ рдлрд╝рд┐рдЯрд┐рдВрдЧ рд░реВрдо рдореЗрдВ рдХрдкрдбрд╝реЗ рдкрд╣рдирдХрд░ рджреЗрдЦрддреЗ рд╣реИрдВ, рддреЛ рдЙрдирдХреЗ рдЦрд░реАрджрдиреЗ рдХреА рд╕рдВрднрд╛рд╡рдирд╛ рдХрд╛рдлрд╝реА рдмрдврд╝ рдЬрд╛рддреА рд╣реИ. рд▓реЗрдХрд┐рди рдСрдирд▓рд╛рдЗрди? рдЖрдкрдиреЗ рд╕рд┐рд░реНрдлрд╝ рдЕрдВрджрд╛рдЬрд╝рд╛ рд▓рдЧрд╛рдпрд╛ рд╣реИ. рдпрд╣ рдкреНрд░реЛрдЬреЗрдХреНрдЯ, рдПрдЖрдИ рдХреА рдорджрдж рд╕реЗ рдЗрд╕ рдЕрдВрддрд░ рдХреЛ рдХрдо рдХрд░рддрд╛ рд╣реИ.

рдЖрд░реНрдХрд┐рдЯреЗрдХреНрдЪрд░ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдЦрд╛рд╕ рдЬрд╛рдирдХрд╛рд░реА

Flutter App  тФАтФАтФАтФА HTTP/REST тФАтФАтФАтФАтЦ╢  ADK Go Backend
                                       тФВ
                            тФМтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФ╝тФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФР
                       Fitting Room  Stylist    Catalog
                         Agent       Agent      Agent
                                       тФВ
                            Gemini API + Cloud Storage

рдореБрдЦреНрдп рдЯреЗрдХреНрдиреЛрд▓реЙрдЬреА

рдХреЙрдореНрдкреЛрдиреЗрдВрдЯ

рдЯреЗрдХреНрдиреЛрд▓реЙрдЬреА

рдордХрд╕рдж

рдПрдЬреЗрдВрдЯ рдлрд╝реНрд░реЗрдорд╡рд░реНрдХ

Go рдХреЗ рд▓рд┐рдП ADK (Agent Development Kit)

рдорд▓реНрдЯреА-рдПрдЬреЗрдВрдЯ рдСрд░реНрдХреЗрд╕реНрдЯреНрд░реЗрд╢рди, рд╕реЗрд╢рди, рдЖрд░реНрдЯрдлрд╝реИрдХреНрдЯ

рдПрдЬреЗрдВрдЯ рдХреЗ рдЬрд╡рд╛рдм рдореЗрдВ рддрд░реНрдХ рд╢рд╛рдорд┐рд▓ рдХрд░рдирд╛ (Pro)

Gemini 3.1 Pro рдХреА рдЭрд▓рдХ

рдпрд╣ рдХреБрдХреА, рдлрд╝рд┐рдЯрд┐рдВрдЧ рд░реВрдо рдФрд░ рд╕реНрдЯрд╛рдЗрд▓рд┐рд╕реНрдЯ рдПрдЬреЗрдВрдЯ рдХреЛ рдореИрдиреЗрдЬ рдХрд░рддреА рд╣реИ

рдПрдЬреЗрдВрдЯ рдХреА рддрд░реНрдХ рдХреНрд╖рдорддрд╛ (рдлрд╝реНрд▓реИрд╢)

Gemini 3 Flash рдХреА рдЭрд▓рдХ

рдпрд╣ рд░реВрдЯ рдФрд░ рдХреИрдЯрд▓реЙрдЧ рдПрдЬреЗрдВрдЯ рдХреЛ рдЪрд╛рд▓реВ рдХрд░рддрд╛ рд╣реИ (рд▓рд╛рдЗрдЯрд╡реЗрдЯ рд░рд╛рдЙрдЯрд┐рдВрдЧ/рд▓реБрдХрдЕрдк)

рдЗрдореЗрдЬ рдЬрдирд░реЗрдЯ рдХрд░рдиреЗ рдХреА рд╕реБрд╡рд┐рдзрд╛

Gemini 2.5 Flash рдХреА рдЗрдореЗрдЬ

рдЗрд╕ рд╕реБрд╡рд┐рдзрд╛ рдХреА рдорджрдж рд╕реЗ, рдХрдкрдбрд╝реЗ рдЖрдЬрд╝рдорд╛рдиреЗ рдФрд░ рдХрдкрдбрд╝реЛрдВ рдХреЗ рдХреЙрдореНрдмрд┐рдиреЗрд╢рди рд╡рд╛рд▓реА рдЗрдореЗрдЬ рдЬрдирд░реЗрдЯ рдХреА рдЬрд╛рддреА рд╣реИрдВ

рдлрд╝реНрд░рдВрдЯрдПрдВрдб

Flutter (Dart)

рдХреНрд░реЙрд╕-рдкреНрд▓реИрдЯрдлрд╝реЙрд░реНрдо рдРрдкреНрд▓рд┐рдХреЗрд╢рди (рд╡реЗрдм, iOS, Android)

рдбрд┐рд╡рд╛рдЗрд╕ рдХрд╛ рд╕реНрдЯреЛрд░реЗрдЬ

Google Cloud Storage

рдпрд╣ рдХреБрдХреА, рдкреНрд░реЙрдбрдХреНрдЯ рдХреА рдЗрдореЗрдЬ рдФрд░ рдЬрдирд░реЗрдЯ рдХрд┐рдП рдЧрдП рдЖрд░реНрдЯрдлрд╝реИрдХреНрдЯ рд╕реЗрд╡ рдХрд░рддреА рд╣реИ

рд╣реЛрд╕реНрдЯрд┐рдВрдЧ

Cloud Run

рд╕рд░реНрд╡рд░рд▓реЗрд╕ рдХрдВрдЯреЗрдирд░ рдбрд┐рдкреНрд▓реЙрдпрдореЗрдВрдЯ

2. ЁЯУж рдЬрд╝рд░реВрд░реА рд╢рд░реНрддреЗрдВ рдФрд░ Cloud Shell рдХрд╛ рд╕реЗрдЯрдЕрдк

1. Cloud Shell Editor рдЦреЛрд▓реЗрдВ

ЁЯСЙ рдЕрдкрдиреЗ рдмреНрд░рд╛рдЙрдЬрд╝рд░ рдореЗрдВ Cloud Shell Editor рдЦреЛрд▓реЗрдВ.

рдЕрдЧрд░ рдЯрд░реНрдорд┐рдирд▓, рд╕реНрдХреНрд░реАрди рдкрд░ рд╕рдмрд╕реЗ рдиреАрдЪреЗ рдирд╣реАрдВ рджрд┐рдЦрддрд╛ рд╣реИ, рддреЛ:

  • рд╡реНрдпреВ тЖТ рдЯрд░реНрдорд┐рдирд▓ рдкрд░ рдХреНрд▓рд┐рдХ рдХрд░реЗрдВ

2. Flutter SDK рдЯреВрд▓ рд╕реЗрдЯ рдЕрдк рдХрд░рдирд╛

Cloud Shell рдореЗрдВ, Flutter рдкрд╣рд▓реЗ рд╕реЗ рдЗрдВрд╕реНрдЯреЙрд▓ рд╣реЛрддрд╛ рд╣реИ. рдпрд╣ /google/flutter рдкрд░ рдореМрдЬреВрдж рд╣реЛрддрд╛ рд╣реИ. рдЙрд╕ рдбрд╛рдпрд░реЗрдХреНрдЯреНрд░реА рдХрд╛ рдорд╛рд▓рд┐рдХрд╛рдирд╛ рд╣рдХ рдХрд┐рд╕реА рджреВрд╕рд░реЗ рд╕рд┐рд╕реНрдЯрдо рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреЗ рдкрд╛рд╕ рд╣реИ. рдЗрд╕рд▓рд┐рдП, рдкрд╣рд▓реА рдмрд╛рд░ flutter рдЪрд▓рд╛рдиреЗ рдкрд░ рдЖрдкрдХреЛ fatal: detected dubious ownership рдЧрдбрд╝рдмрдбрд╝реА рджрд┐рдЦреЗрдЧреА. рдЗрд╕реЗ git рдХреА safe-directory рд╕реВрдЪреА рдореЗрдВ рдПрдХ рдмрд╛рд░ рдЬреЛрдбрд╝реЗрдВ:

git config --global --add safe.directory /google/flutter

рдкреБрд╖реНрдЯрд┐ рдХрд░реЗрдВ рдХрд┐ Flutter рдЖрдкрдХреЗ PATH рдкрд░ рдореМрдЬреВрдж рд╣реИ рдФрд░ рдХрд╛рдо рдХрд░ рд░рд╣рд╛ рд╣реИ:

flutter --version

рдкрд╣рд▓реА рдмрд╛рд░ рдЪрд▓рд╛рдиреЗ рдкрд░, Dart SDK рдбрд╛рдЙрдирд▓реЛрдб рд╣реЛрддрд╛ рд╣реИ рдФрд░ Flutter рдЯреВрд▓ рдмрдирддрд╛ рд╣реИ. рдЗрд╕рдореЗрдВ рдПрдХ рдорд┐рдирдЯ рд▓рдЧрддрд╛ рд╣реИ. рдЖрдкрдХреЛ Flutter 3.x тАв channel stable рдЬреИрд╕рд╛ рдХреБрдЫ рджрд┐рдЦреЗрдЧрд╛.

3. рд░рд┐рдкреЙрдЬрд╝рд┐рдЯрд░реА рдХрд╛ рдХреНрд▓реЛрди рдмрдирд╛рдирд╛

cd ~
git clone https://github.com/gca-americas/fashion-app-demo
cd fashion_app_demo

4. рдкреНрд░реЛрдЬреЗрдХреНрдЯ рд╕реНрдЯреНрд░рдХреНрдЪрд░ рдПрдХреНрд╕рдкреНрд▓реЛрд░ рдХрд░рдирд╛

fashion_app_demo/
тФЬтФАтФА adk_backend/                 # Go backend with ADK agents
тФВ   тФЬтФАтФА main.go                  # Entry point тАФ wires all agents + REST server
тФВ   тФЬтФАтФА catalog/                 # Catalog Agent тАФ product lookup
тФВ   тФВ   тФЬтФАтФА agent.go
тФВ   тФВ   тФЬтФАтФА catalog.yaml         # Product database (YAML)
тФВ   тФВ   тФФтФАтФА instructions.md      # Agent persona prompt
тФВ   тФЬтФАтФА fittingroom/             # Fitting Room Agent тАФ virtual try-on
тФВ   тФВ   тФЬтФАтФА agent.go
тФВ   тФВ   тФЬтФАтФА instructions.md      # Agent persona prompt
тФВ   тФВ   тФФтФАтФА tool_instructions.md # Image generation prompt
тФВ   тФЬтФАтФА stylist/                 # Stylist Agent тАФ outfit curation
тФВ   тФВ   тФЬтФАтФА agent.go
тФВ   тФВ   тФФтФАтФА instructions.md      # Agent persona + output format
тФВ   тФЬтФАтФА rootagent/               # Root Agent тАФ routes to the right agent
тФВ   тФВ   тФФтФАтФА agent.go
тФВ   тФФтФАтФА tools/                   # Shared tools
тФВ       тФЬтФАтФА imagetool.go         # getProductImage тАФ loads product images
тФВ       тФЬтФАтФА outfit_gen_tool.go   # generate_outfit_image тАФ creates outfit images
тФВ       тФФтФАтФА cors_helper.go      # CORS middleware + request logging
тФВ
тФЬтФАтФА flutter_frontend/            # Flutter cross-platform app
тФВ   тФЬтФАтФА lib/
тФВ   тФВ   тФЬтФАтФА main.dart            # App entry point + Provider setup
тФВ   тФВ   тФЬтФАтФА app_config.dart      # Backend URL configuration
тФВ   тФВ   тФЬтФАтФА core_app/            # Pre-built shopping app (browse, cart, etc.)
тФВ   тФВ   тФФтФАтФА workshop_tasks/      # AI feature code
тФВ   тФВ       тФЬтФАтФА step_1_try_it_on/  # Virtual Try-On flow
тФВ   тФВ       тФВ   тФЬтФАтФА providers/     # TryItOnProvider (state management)
тФВ   тФВ       тФВ   тФЬтФАтФА services/      # AdkFittingRoomService (HTTP calls)
тФВ   тФВ       тФВ   тФФтФАтФА ui/            # Screens (product detail тЖТ try on тЖТ fitting room)
тФВ   тФВ       тФФтФАтФА step_2_style_me/   # Style Me flow
тФВ   тФВ           тФЬтФАтФА models/        # Outfit, StyleRequest data classes
тФВ   тФВ           тФЬтФАтФА providers/     # StylingProvider (state management)
тФВ   тФВ           тФЬтФАтФА services/      # AdkStylingService (HTTP calls)
тФВ   тФВ           тФФтФАтФА ui/            # Screens (form sheet тЖТ outfit carousel)
тФВ   тФФтФАтФА assets/images/           # Product catalog images

3. тШБя╕П Google Cloud рдкреНрд░реЛрдЬреЗрдХреНрдЯ рдХрд╛ рд╕реЗрдЯрдЕрдк

1. рдирдпрд╛ рдкреНрд░реЛрдЬреЗрдХреНрдЯ рдмрдирд╛рдирд╛

gcloud projects create fashion-app-demo --name="Fashion App Demo"
gcloud config set project fashion-app-demo

рдЕрдкрдиреЗ рдмрд┐рд▓рд┐рдВрдЧ рдЦрд╛рддреЛрдВ рдХреА рд╕реВрдЪреА рдмрдирд╛рдПрдВ:

gcloud billing accounts list

OPEN

рдХреЙрд▓рдо рдореЗрдВ рдореМрдЬреВрдж рд╣реИ. рдЗрд╕рдореЗрдВ True рд▓рд┐рдЦрд╛ рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдП. рдЕрдЧрд░ рдЖрдкрдХреЛ False рджрд┐рдЦрддрд╛ рд╣реИ (рдпрд╣ рд╕рдорд╕реНрдпрд╛, рдмрд┐рдирд╛ рд╢реБрд▓реНрдХ рдЖрдЬрд╝рдорд╛рдиреЗ рдХреА рдЕрд╡рдзрд┐ рдЦрддреНрдо рд╣реЛрдиреЗ рдкрд░ рдЖрдо рддреМрд░ рдкрд░ рджрд┐рдЦрддреА рд╣реИ), рддреЛ рдЗрд╕рдХрд╛ рдорддрд▓рдм рд╣реИ рдХрд┐ рдЦрд╛рддрд╛ рдмрдВрдж рд╣реЛ рдЧрдпрд╛ рд╣реИ рдФрд░ рдЖрдкрд╕реЗ рдХрд┐рд╕реА рднреА рдЪреАрдЬрд╝ рдХреЗ рд▓рд┐рдП рд╢реБрд▓реНрдХ рдирд╣реАрдВ рд▓рд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛. рдЖрдЧреЗ рдмрдврд╝рдиреЗ рд╕реЗ рдкрд╣рд▓реЗ, рдиреАрдЪреЗ рджрд┐рдП рдЧрдП рд╕рдорд╕реНрдпрд╛ рд╣рд▓ рдХрд░рдиреЗ рд╕реЗ рдЬреБрдбрд╝реЗ рдмреНрд▓реЙрдХ рдкрд░ рдЬрд╛рдПрдВ.

ACCOUNT_ID рдЦрд╛рддреЗ рдХрд╛ ACCOUNT_ID (0X0X0X-0X0X0X-0X0X0X рдЬреИрд╕рд╛ рджрд┐рдЦрддрд╛ рд╣реИ) рдХреЙрдкреА рдХрд░реЗрдВ рдФрд░ рдЙрд╕реЗ рдЕрдкрдиреЗ рдкреНрд░реЛрдЬреЗрдХреНрдЯ рд╕реЗ рд▓рд┐рдВрдХ рдХрд░реЗрдВ:OPEN: True

gcloud billing projects link fashion-app-demo \
 --billing-account=YOUR_BILLING_ACCOUNT_ID

рд▓рд┐рдВрдХ рдХреА рдкреБрд╖реНрдЯрд┐ рдХрд░реЗрдВ:

gcloud billing projects describe fashion-app-demo

рдЖрдкрдХреЛ billingEnabled: true рджрд┐рдЦреЗрдЧрд╛. рдЕрдЧрд░ рдЦрд╛рддрд╛ рд▓рд┐рдВрдХ рдХрд░рдиреЗ рдХреЗ рдмрд╛рдж рднреА рдЖрдкрдХреЛ billingEnabled: false рджрд┐рдЦрддрд╛ рд╣реИ, рддреЛ рдЗрд╕рдХрд╛ рдорддрд▓рдм рд╣реИ рдХрд┐ рдЦрд╛рддрд╛ рдмрдВрдж рд╣реИ (OPEN: False). рдЗрд╕ рд╕рдорд╕реНрдпрд╛ рдХреЛ рд╣рд▓ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдпрд╣рд╛рдВ рджрд┐рдпрд╛ рдЧрдпрд╛ рддрд░реАрдХрд╛ рджреЗрдЦреЗрдВ.

3. рдЬрд╝рд░реВрд░реА рдПрдкреАрдЖрдИ рдЪрд╛рд▓реВ рдХрд░рдирд╛

gcloud services enable \
 aiplatform.googleapis.com \
 storage.googleapis.com \
 run.googleapis.com \
 cloudbuild.googleapis.com \
 artifactregistry.googleapis.com

рдПрдкреАрдЖрдИ

рдордХрд╕рдж

aiplatform.googleapis.com

Vertex AI тАФ fitting_tool, Vertex AI рдХреЗ рдЬрд╝рд░рд┐рдП Gemini рдХреА рдЗрдореЗрдЬ рдЬрдирд░реЗрд╢рди рд╕реБрд╡рд┐рдзрд╛ рдХрд╛ рдЗрд╕реНрддреЗрдорд╛рд▓ рдХрд░рддрд╛ рд╣реИ

storage.googleapis.com

Cloud Storage тАФ рдЗрд╕рдореЗрдВ рдкреНрд░реЙрдбрдХреНрдЯ рдХреИрдЯрд▓реЙрдЧ рдХреА рдЗрдореЗрдЬ рдФрд░ рдЖрдЬрд╝рдорд╛рдиреЗ рдХреЗ рдЬрдирд░реЗрдЯ рдХрд┐рдП рдЧрдП рдирддреАрдЬреЗ рд╕реЗрд╡ рдХрд┐рдП рдЬрд╛рддреЗ рд╣реИрдВ

run.googleapis.com

Cloud Run тАФ рдмреИрдХрдПрдВрдб рдХреЛ рд╕рд░реНрд╡рд░рд▓реЗрд╕ рдХрдВрдЯреЗрдирд░ рдХреЗ рддреМрд░ рдкрд░ рд╣реЛрд╕реНрдЯ рдХрд░рддрд╛ рд╣реИ

cloudbuild.googleapis.com

Cloud Build тАФ рдпрд╣ рд╕реЛрд░реНрд╕ рд╕реЗ Docker рдЗрдореЗрдЬ рдмрдирд╛рддрд╛ рд╣реИ

artifactregistry.googleapis.com

Artifact Registry тАФ рдЗрд╕рдореЗрдВ рдмрдирд╛рдИ рдЧрдИ Docker рдЗрдореЗрдЬ рд╕реЗрд╡ рд╣реЛрддреА рд╣реИрдВ

4. GCS рдмрдХреЗрдЯ рдмрдирд╛рдирд╛

export PROJECT_ID=$(gcloud config get-value project)
gcloud storage buckets create gs://fashion-app-$PROJECT_ID \
 --location=us-central1 \
 --uniform-bucket-level-access

5. рдкреНрд░реЙрдбрдХреНрдЯ рдХреИрдЯрд▓реЙрдЧ рдХреА рдЗрдореЗрдЬ рдЕрдкрд▓реЛрдб рдХрд░рдирд╛

рдмреИрдХрдПрдВрдб рдХрд╛ getProductImage рдЯреВрд▓, gs://$GCS_BUCKET/catalog-assets/images/ рд╕реЗ рдбреЗрдЯрд╛ рдкрдврд╝рддрд╛ рд╣реИ. рдХреИрдЯрд▓реЙрдЧ рдХреА рдЗрдореЗрдЬ рдХреЛ рдЗрд╕ рдкрд╛рде рдкрд░ рдЕрдкрд▓реЛрдб рдХрд░реЗрдВ:

cd ~/fashion_app_demo
gcloud storage cp flutter_frontend/assets/images/*.png \
 gs://fashion-app-$PROJECT_ID/catalog-assets/images/

рдЕрдкрд▓реЛрдб рдХреА рдЧрдИ рдлрд╝рд╛рдЗрд▓реЛрдВ рдХреА рдкреБрд╖реНрдЯрд┐ рдХрд░реЗрдВ. рдЖрдкрдХреЛ .png рдлрд╝рд╛рдЗрд▓реЛрдВ рдХреА рд╕реВрдЪреА рджрд┐рдЦреЗрдЧреА:

gcloud storage ls gs://fashion-app-$PROJECT_ID/catalog-assets/images/

6. .env рдлрд╝рд╛рдЗрд▓ рдХреЛ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░ рдХрд░рдирд╛

cd ~/fashion_app_demo/adk_backend
cat > .env << EOF
GOOGLE_CLOUD_PROJECT=$PROJECT_ID
GCS_BUCKET=fashion-app-$PROJECT_ID
EOF

7. рдРрдкреНрд▓рд┐рдХреЗрд╢рди рдХреЗ рдбрд┐рдлрд╝реЙрд▓реНрдЯ рдХреНрд░реЗрдбреЗрдВрд╢рд┐рдпрд▓ рдХреА рдорджрдж рд╕реЗ рдкреБрд╖реНрдЯрд┐ рдХрд░рдирд╛

рдЖрдкрдХреЛ рдмреИрдХрдПрдВрдб рдХреЛ рд╕реНрдерд╛рдиреАрдп рддреМрд░ рдкрд░ рд╢реБрд░реВ рдХрд░рдиреЗ рд╕реЗ рдкрд╣рд▓реЗ, рдЗрд╕реЗ рдЪрд▓рд╛рдирд╛ рд╣реЛрдЧрд╛. Go рдмреИрдХрдПрдВрдб, Vertex AI (Gemini) рдФрд░ Cloud Storage рдХреЛ рдХрд┐рдП рдЧрдП рд╣рд░ рдХреЙрд▓ рдХреА рдкреБрд╖реНрдЯрд┐ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдПрдбреАрд╕реА рдХрд╛ рдЗрд╕реНрддреЗрдорд╛рд▓ рдХрд░рддрд╛ рд╣реИ. рдПрдбреАрд╕реА рдХреЗ рдмрд┐рдирд╛, рдмреИрдХрдПрдВрдб рд╢реБрд░реВ рд╣реЛ рдЬрд╛рдПрдЧрд╛. рд╣рд╛рд▓рд╛рдВрдХрд┐, рдЖрдЬрд╝рдорд╛рдиреЗ рдХреЗ рд╣рд░ рдЕрдиреБрд░реЛрдз рдкрд░ 401 CREDENTIALS_MISSING рдЧрдбрд╝рдмрдбрд╝реА рджрд┐рдЦреЗрдЧреА.

рдПрдХ рдХреНрд░реЗрдбреЗрдВрд╢рд┐рдпрд▓ рд╕реЗ рджреЛрдиреЛрдВ рд╕реЗрд╡рд╛рдУрдВ рдХреЛ рдРрдХреНрд╕реЗрд╕ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ. рдЗрди рджреЛрдиреЛрдВ рдирд┐рд░реНрджреЗрд╢реЛрдВ рдХреЛ рдЗрд╕реА рдХреНрд░рдо рдореЗрдВ рдЪрд▓рд╛рдПрдВ:

# 1. Log in (opens a browser; in Cloud Shell, paste the verification code back)
gcloud auth application-default login

# 2. Attach your project as the quota / billing project for ADC
gcloud auth application-default set-quota-project $(gcloud config get-value project)

рдкреБрд╖реНрдЯрд┐ рдХрд░реЗрдВ рдХрд┐ рдПрдбреАрд╕реА рд╕рд╣реА рддрд░реАрдХреЗ рд╕реЗ рдХрд╛рдо рдХрд░ рд░рд╣рд╛ рд╣реИ:

gcloud auth application-default print-access-token | head -c 20 && echo "..."

рдЖрдкрдХреЛ рдЯреЛрдХрди рдХреЗ ~20 рд╡рд░реНрдгреЛрдВ рдХреЗ рдмрд╛рдж ... рджрд┐рдЦреЗрдЧрд╛. рдЕрдЧрд░ рдЧрдбрд╝рдмрдбрд╝реА рд╣реЛрддреА рд╣реИ, рддреЛ рдЗрд╕рдХрд╛ рдорддрд▓рдм рд╣реИ рдХрд┐ рд▓реЙрдЧрд┐рди рдирд╣реАрдВ рд╣реБрдЖ рд╣реИ. рдкрд╣рд▓реЗ рдЪрд░рдг рдХреЛ рдлрд┐рд░ рд╕реЗ рдЪрд▓рд╛рдПрдВ.

4. ЁЯПЧя╕П рдЖрд░реНрдХрд┐рдЯреЗрдХреНрдЪрд░ рдХреА рдЦрд╛рд╕ рдЬрд╛рдирдХрд╛рд░реА

рдЕрдм рдПрдирд╡рд╛рдпрд░рдореЗрдВрдЯ рддреИрдпрд╛рд░ рд╣реИ. рдЗрд╕рд▓рд┐рдП, рдХреЛрдб рджреЗрдЦрдиреЗ рд╕реЗ рдкрд╣рд▓реЗ рдпрд╣ рд╕рдордЭрддреЗ рд╣реИрдВ рдХрд┐ рд╕рд┐рд╕реНрдЯрдо рдХреИрд╕реЗ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИ.

рдЪрд╛рд░ рдПрдЬреЗрдВрдЯ рд╡рд╛рд▓рд╛ рд╕рд┐рд╕реНрдЯрдо

рдмреИрдХрдПрдВрдб рдХреЛ Go рдХреЗ рд▓рд┐рдП ADK (Agent Development Kit) рдХрд╛ рдЗрд╕реНрддреЗрдорд╛рд▓ рдХрд░рдХреЗ, рдорд▓реНрдЯреА-рдПрдЬреЗрдВрдЯ рд╕рд┐рд╕реНрдЯрдо рдХреЗ рддреМрд░ рдкрд░ рдмрдирд╛рдпрд╛ рдЧрдпрд╛ рд╣реИ. рдЗрд╕рдореЗрдВ рдЪрд╛рд░ рдПрдЬреЗрдВрдЯ рдПрдХ рд╕рд╛рде рдХрд╛рдо рдХрд░рддреЗ рд╣реИрдВ. рд╣рд░ рдПрдЬреЗрдВрдЯ рдХреА рдПрдХ рдЦрд╛рд╕ рдЬрд╝рд┐рдореНрдореЗрджрд╛рд░реА рд╣реЛрддреА рд╣реИ:

                   тФМтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФР
                   тФВ Root Agent   тФВ  тЖР Routes requests to the right agent
                   тФВ (gemini-3-   тФВ
                   тФВ  flash-      тФВ
                   тФВ  preview)    тФВ
                   тФФтФАтФАтФАтФАтФАтФАтФмтФАтФАтФАтФАтФАтФАтФАтФШ
                          тФВ
           тФМтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФ╝тФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФР
           тЦ╝              тЦ╝              тЦ╝
  тФМтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФР тФМтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФР тФМтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФР
  тФВ Fitting Room   тФВ тФВ Catalog  тФВ тФВ Stylist        тФВ
  тФВ Agent          тФВ тФВ Agent    тФВ тФВ Agent          тФВ
  тФВ (gemini-3.1-   тФВ тФВ (gemini- тФВ тФВ (gemini-3.1-   тФВ
  тФВ  pro-preview)  тФВ тФВ  3-flash-тФВ тФВ  pro-preview)  тФВ
  тФВ                тФВ тФВ  preview)тФВ тФВ                тФВ
  тФВ Tools:         тФВ тФВ          тФВ тФВ Tools:         тФВ
  тФВ тАв fitting_tool тФВ тФВ Tools:   тФВ тФВ тАв fitting_tool тФВ
  тФВ тАв getProduct   тФВ тФВ тАв list   тФВ тФВ тАв getProduct   тФВ
  тФВ   Image        тФВ тФВ Products тФВ тФВ   Image        тФВ
  тФВ тАв catalog_agentтФВ тФВ тАв get    тФВ тФВ тАв catalog_agentтФВ
  тФВ   (delegation) тФВ тФВ Product  тФВ тФВ   (delegation) тФВ
  тФВ                тФВ тФВ  Image   тФВ тФВ тАв generate_    тФВ
  тФФтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФШ тФФтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФШ тФВ   outfit_image тФВ
                                  тФФтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФШ

рдПрдЬреЗрдВрдЯ

рдореЙрдбрд▓

рднреВрдорд┐рдХрд╛

рд░реВрдЯ рдПрдЬреЗрдВрдЯ

gemini-3-flash-preview

рдЯреНрд░реИрдлрд╝рд┐рдХ рдкреБрд▓рд┐рд╕. рдпрд╣ рдХреБрдХреА, рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреЗ рдореИрд╕реЗрдЬ рдХреЛ рдкрдврд╝рддреА рд╣реИ рдФрд░ рдЙрд╕реЗ рд╕рд╣реА рд╕реНрдкреЗрд╢рд▓рд┐рд╕реНрдЯ рдПрдЬреЗрдВрдЯ рдХреЛ рд╕реМрдВрдкрддреА рд╣реИ. рдпрд╣ рддреЗрдЬрд╝ рдФрд░ рдХрдо рдореЗрдореЛрд░реА рд╡рд╛рд▓рд╛ рдореЙрдбрд▓ рдЗрд╕реНрддреЗрдорд╛рд▓ рдХрд░рддрд╛ рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рдЗрд╕реЗ рд╕рд┐рд░реНрдлрд╝ рд░рд╛рдЙрдЯрд┐рдВрдЧ рдХреЗ рдлрд╝реИрд╕рд▓реЗ рд▓реЗрдиреЗ рд╣реЛрддреЗ рд╣реИрдВ.

рдХреИрдЯрд▓реЙрдЧ рдПрдЬреЗрдВрдЯ

gemini-3-flash-preview

рдкреНрд░реЙрдбрдХреНрдЯ рдПрдХреНрд╕рдкрд░реНрдЯ. рдпрд╣ YAML рдлрд╝рд╛рдЗрд▓ рд╕реЗ рдкреНрд░реЙрдбрдХреНрдЯ рдХреИрдЯрд▓реЙрдЧ рд▓реЛрдб рдХрд░рддрд╛ рд╣реИ рдФрд░ рдкреНрд░реЙрдбрдХреНрдЯ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдкреВрдЫреЗ рдЧрдП рд╕рд╡рд╛рд▓реЛрдВ рдХреЗ рдЬрд╡рд╛рдм рджреЗрддрд╛ рд╣реИ. рдпрд╣ рдХрдо рд╕рдВрд╕рд╛рдзрдиреЛрдВ рдХрд╛ рдЗрд╕реНрддреЗрдорд╛рд▓ рдХрд░рддрд╛ рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рдпрд╣ рд╕рд┐рд░реНрдлрд╝ рдбреЗрдЯрд╛ рдвреВрдВрдврддрд╛ рд╣реИ.

рдлрд╝рд┐рдЯрд┐рдВрдЧ рд░реВрдо рдПрдЬреЗрдВрдЯ

gemini-3.1-pro-preview

рд╡рд░реНрдЪреБрдЕрд▓ рддреМрд░ рдкрд░ рдЖрдЬрд╝рдорд╛рдиреЗ рдХреА рд╕реБрд╡рд┐рдзрд╛ рдХреЗ рд╡рд┐рд╢реЗрд╖рдЬреНрдЮ. рдпрд╣ рд╕реБрд╡рд┐рдзрд╛, рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреА рдлрд╝реЛрдЯреЛ рдФрд░ рдкреНрд░реЙрдбрдХреНрдЯ рдХреА рдЗрдореЗрдЬ рд▓реЗрддреА рд╣реИ. рдЗрд╕рдХреЗ рдмрд╛рдж, рдПрдХ рдХрдВрдкреЛрдЬрд╝рд┐рдЯ рдЗрдореЗрдЬ рдЬрдирд░реЗрдЯ рдХрд░рддреА рд╣реИ, рдЬрд┐рд╕рдореЗрдВ рд╡реНрдпрдХреНрддрд┐ рдХреЛ рд╡рд╣ рдкреНрд░реЙрдбрдХреНрдЯ рдкрд╣рдиреЗ рд╣реБрдП рджрд┐рдЦрд╛рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ. рдпрд╣ рдЬрд╝реНрдпрд╛рджрд╛ рдмреЗрд╣рддрд░ рдореЙрдбрд▓ рдХрд╛ рдЗрд╕реНрддреЗрдорд╛рд▓ рдХрд░рддрд╛ рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рдЗрд╕реЗ рдЗрдореЗрдЬ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рддрд░реНрдХ рджреЗрдирд╛ рд╣реЛрддрд╛ рд╣реИ.

рд╕реНрдЯрд╛рдЗрд▓рд┐рд╢ рдПрдЬреЗрдВрдЯ

gemini-3.1-pro-preview

рдлрд╝реИрд╢рди рд╕рд▓рд╛рд╣рдХрд╛рд░. рдпрд╣ рдЬрдЧрд╣, рдЕрд╡рд╕рд░, рдФрд░ рдкреНрд░рд╛рдердорд┐рдХрддрд╛рдУрдВ рдХреЗ рд╣рд┐рд╕рд╛рдм рд╕реЗ, рдХреИрдЯрд▓реЙрдЧ рдореЗрдВ рдореМрдЬреВрдж рдХрдкрдбрд╝реЛрдВ рдХреЗ рддреАрди рдХреЙрдореНрдмрд┐рдиреЗрд╢рди рддреИрдпрд╛рд░ рдХрд░рддрд╛ рд╣реИ. рд╣рд░ рдкреЛрд╢рд╛рдХ рдХреЗ рд▓рд┐рдП, рдЖрдЬрд╝рдорд╛рдиреЗ рд╡рд╛рд▓реА рдЗрдореЗрдЬ рдЬрдирд░реЗрдЯ рдХрд░ рд╕рдХрддрд╛ рд╣реИ. рдпрд╣ рдХреНрд░рд┐рдПрдЯрд┐рд╡ рд░реАрдЬрд╝рдирд┐рдВрдЧ рдХреЗ рд▓рд┐рдП, рдмреЗрд╣рддрд░ рдореЙрдбрд▓ рдХрд╛ рдЗрд╕реНрддреЗрдорд╛рд▓ рдХрд░рддрд╛ рд╣реИ.

рдПрдВрдЯреНрд░реА рдкреЙрдЗрдВрдЯ: main.go

рд╕рдм рдХреБрдЫ main.go рд╕реЗ рд╢реБрд░реВ рд╣реЛрддрд╛ рд╣реИ. рдпрд╣ рдПрдЬреЗрдВрдЯ рдХреЛ рдПрдХ рд╕рд╛рде рдЬреЛрдбрд╝рддрд╛ рд╣реИ рдФрд░ рдПрдЪрдЯреАрдЯреАрдкреА рд╕рд░реНрд╡рд░ рд╢реБрд░реВ рдХрд░рддрд╛ рд╣реИ:

// main.go тАФ simplified for clarity


func main() {
   godotenv.Load()  // Load .env file


   // 1. Create the artifact storage (GCS-backed)
   artifacts, _ := gcsartifact.NewService(ctx, bucket)


   // 2. Build agents bottom-up (dependencies first)
   catagent, _    := catalog.NewCatalogAgent(apikey, "catalog/catalog.yaml")
   fitagent, _    := fittingroom.NewFittingRoomAgent(apikey, catagent)
   stylistAgent, _ := stylist.NewStylistAgent(apikey, catagent)
   ragent, _      := rootagent.NewRootAgent(apikey, fitagent, catagent, stylistAgent)


   // 3. Register all agents with a multi-loader
   loader, _ := agent.NewMultiLoader(ragent, fitagent, catagent, stylistAgent)


   // 4. Create the ADK REST server
   restHandler, _ := adkrest.NewServer(adkrest.ServerConfig{
       SessionService:  session.InMemoryService(),
       MemoryService:   memory.InMemoryService(),
       AgentLoader:     loader,
       ArtifactService: artifacts,
   })


   // 5. Mount behind /api/ with CORS support
   r := mux.NewRouter()
   r.Use(tools.LocalhostCORS)
   r.PathPrefix("/api/").Handler(
       http.StripPrefix("/api", tools.LogHandler(restHandler)))


   http.Server{Addr: ":8080", Handler: r}.ListenAndServe()
}

рдзреНрдпрд╛рди рджреЗрдиреЗ рд▓рд╛рдпрдХ рдХреБрдЫ рдЬрд╝рд░реВрд░реА рдмрд╛рддреЗрдВ:

  • рдПрдЬреЗрдВрдЯ рдмреЙрдЯрдо-рдЕрдк рддрд░реАрдХреЗ рд╕реЗ рдмрдирд╛рдП рдЬрд╛рддреЗ рд╣реИрдВ: рдХреИрдЯрд▓реЙрдЧ рдПрдЬреЗрдВрдЯ рдХреЛ рд╕рдмрд╕реЗ рдкрд╣рд▓реЗ рдмрдирд╛рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рдлрд╝рд┐рдЯрд┐рдВрдЧ рд░реВрдо рдФрд░ рд╕реНрдЯрд╛рдЗрд▓рд┐рд╕реНрдЯ рдПрдЬреЗрдВрдЯ, рджреЛрдиреЛрдВ рдЗрд╕ рдкрд░ рдирд┐рд░реНрднрд░ рдХрд░рддреЗ рд╣реИрдВ. рдпреЗ рджреЛрдиреЛрдВ рдПрдЬреЗрдВрдЯ, рдкреНрд░реЙрдбрдХреНрдЯ рдХреА рдЬрд╛рдирдХрд╛рд░реА рдЦреЛрдЬрдиреЗ рдХрд╛ рдХрд╛рдо рдХреИрдЯрд▓реЙрдЧ рдПрдЬреЗрдВрдЯ рдХреЛ рд╕реМрдВрдкрддреЗ рд╣реИрдВ.
  • agent.NewMultiLoader рдЪрд╛рд░реЛрдВ рдПрдЬреЗрдВрдЯ рдХреЛ рд░рдЬрд┐рд╕реНрдЯрд░ рдХрд░рддрд╛ рд╣реИ, рддрд╛рдХрд┐ REST API рдирд╛рдо рдХреЗ рд╣рд┐рд╕рд╛рдм рд╕реЗ рдХрд┐рд╕реА рднреА рдПрдЬреЗрдВрдЯ рдХреЛ рд░реВрдЯ рдХрд░ рд╕рдХреЗ.
  • adkrest.NewServer, REST API рдЕрдкрдиреЗ-рдЖрдк рдЙрдкрд▓рдмреНрдз рдХрд░рд╛рддрд╛ рд╣реИ. рдЖрдкрдХреЛ рдПрдВрдбрдкреЙрдЗрдВрдЯ рд╣реИрдВрдбрд▓рд░ рдЦреБрдж рдирд╣реАрдВ рд▓рд┐рдЦрдиреЗ рд╣реЛрддреЗ. ADK рдореЗрдВ, рд╕реЗрд╢рди рдореИрдиреЗрдЬрдореЗрдВрдЯ, рдЖрд░реНрдЯрдлрд╝реИрдХреНрдЯ рд╕реНрдЯреЛрд░реЗрдЬ, рдФрд░ рдПрдЬреЗрдВрдЯ рдПрдХреНрдЬрд╝реАрдХреНрдпреВрд╢рди рдХреА рд╕реБрд╡рд┐рдзрд╛ рдкрд╣рд▓реЗ рд╕реЗ рдореМрдЬреВрдж рд╣реЛрддреА рд╣реИ.
  • рдпрд╣ рдХреБрдХреА, рд╕реЗрд╢рди рдХреА рдЬрд╛рдирдХрд╛рд░реА рдХреЛ рдореЗрдореЛрд░реА рдореЗрдВ рд╕реЗрд╡ рдХрд░рддреА рд╣реИ. session.InMemoryService() рдЗрд╕рдХрд╛ рдорддрд▓рдм рд╣реИ рдХрд┐ рд╕рд░реНрд╡рд░ рд░реАрд╕реНрдЯрд╛рд░реНрдЯ рд╣реЛрдиреЗ рдкрд░ рд╕реЗрд╢рди рдмрдВрдж рд╣реЛ рдЬрд╛рддреЗ рд╣реИрдВ. рдбреЗрдореЛ рдХреЗ рд▓рд┐рдП рдпрд╣ рдареАрдХ рд╣реИ. рдкреНрд░реЛрдбрдХреНрд╢рди рдореЗрдВ, рдЖрдкрдХреЛ рдкрд░рд╕рд┐рд╕реНрдЯреЗрдВрдЯ рд╕реНрдЯреЛрд░ рдХрд╛ рдЗрд╕реНрддреЗрдорд╛рд▓ рдХрд░рдирд╛ рд╣реЛрдЧрд╛.
  • gcsartifact.NewService, Google Cloud Storage рдореЗрдВ рдЖрд░реНрдЯрдлрд╝реИрдХреНрдЯ (рдЬрдирд░реЗрдЯ рдХреА рдЧрдИ рдЗрдореЗрдЬ) рд╕реЗрд╡ рдХрд░рддрд╛ рд╣реИ, рддрд╛рдХрд┐ рд╡реЗ рд╕рднреА рдЕрдиреБрд░реЛрдзреЛрдВ рдореЗрдВ рдмрдиреЗ рд░рд╣реЗрдВ. рд╕рд╛рде рд╣реА, рдЙрдиреНрд╣реЗрдВ GCS рдпреВрдЖрд░рдЖрдИ рдХреЗ рдЬрд╝рд░рд┐рдП рд╢реЗрдпрд░ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХреЗ.

5. ЁЯдЦ ADK (Agent Development Kit) рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдЬрд╝реНрдпрд╛рджрд╛ рдЬрд╛рдирдХрд╛рд░реА

рдПрдбреАрдХреЗ рдХреНрдпрд╛ рд╣реИ?

Agent Development Kit (ADK), Google рдХрд╛ рдПрдХ рдУрдкрди-рд╕реЛрд░реНрд╕ рдлрд╝реНрд░реЗрдорд╡рд░реНрдХ рд╣реИ. рдЗрд╕рдХреА рдорджрдж рд╕реЗ, Go (рдФрд░ Python/Java) рдореЗрдВ рдПрдЖрдИ рдПрдЬреЗрдВрдЯ рдмрдирд╛рдП рдЬрд╛ рд╕рдХрддреЗ рд╣реИрдВ. рдпрд╣ рдЖрдкрдХреЗ рдРрдкреНрд▓рд┐рдХреЗрд╢рди рдФрд░ Gemini API рдХреЗ рдмреАрдЪ рдХреА рд▓реЗрдпрд░ рд╣реИ.

Gemini API рдХреЛ рд╕реАрдзреЗ рддреМрд░ рдкрд░ рдХреЙрд▓ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ. рд╣рд╛рд▓рд╛рдВрдХрд┐, рдЬрдм рдЖрдкрдХреЗ рдРрдкреНрд▓рд┐рдХреЗрд╢рди рдХреЛ:

  • рдХреИрдЯрд▓реЙрдЧ рдореЗрдВ рдореМрдЬреВрдж рдкреНрд░реЙрдбрдХреНрдЯ рдЦреЛрдЬрдирд╛
  • рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреА рдлрд╝реЛрдЯреЛ рдХреЗ рдЖрдзрд╛рд░ рдкрд░ рдЗрдореЗрдЬ рдЬрдирд░реЗрдЯ рдХрд░рдирд╛
  • рдпрд╣ рдпрд╛рдж рд░рдЦрдирд╛ рдХрд┐ рдкрд╣рд▓реЗ рдХреМрдирд╕реЗ рдХрдкрдбрд╝реЗ рд╕реБрдЭрд╛рдП рдЧрдП рдереЗ
  • рдПрдХ рд╕реЗ рдЬрд╝реНрдпрд╛рджрд╛ рдПрдЖрдИ рдПрдЬреЗрдВрдЯ рдХреЗ рд╕рд╛рде рдорд┐рд▓рдХрд░ рдХрд╛рдо рдХрд░рдирд╛

рдЖрдкрдХреЛ рд╕реНрдЯреНрд░рдХреНрдЪрд░ рдХреА рдЬрд╝рд░реВрд░рдд рд╣реИ. ADK, рдпрд╣ рд╕реНрдЯреНрд░рдХреНрдЪрд░ рдЙрдкрд▓рдмреНрдз рдХрд░рд╛рддрд╛ рд╣реИ.

рдПрдЬреЗрдВрдЯ рд▓реВрдк

рд╣рд░ рдПрдбреАрдХреЗ рдПрдЬреЗрдВрдЯ, рдЗрд╕ рд▓реВрдк рдХреЛ рдлрд╝реЙрд▓реЛ рдХрд░рддрд╛ рд╣реИ:

1. Receive a message (from user or another agent)
2. Think тАФ the LLM reasons about what to do
3. Act тАФ call a tool, delegate to a sub-agent, or respond
4. Return тАФ send the result back

рдпрд╣ рд▓реВрдк, рдПрдХ рд╣реА рдЕрдиреБрд░реЛрдз рдореЗрдВ рдХрдИ рдмрд╛рд░ рджреЛрд╣рд░рд╛рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ. рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рд╕реНрдЯрд╛рдЗрд▓рд┐рд╕реНрдЯ рдПрдЬреЗрдВрдЯ рдпреЗ рдХрд╛рдо рдХрд░ рд╕рдХрддрд╛ рд╣реИ:

  1. "рд╕рдореБрджреНрд░ рддрдЯ рдкрд░ рдЫреБрдЯреНрдЯрд┐рдпрд╛рдВ рдмрд┐рддрд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдореБрдЭреЗ рд╕реНрдЯрд╛рдЗрд▓ рдХрд░реЛ"
  2. рдкреНрд░реЙрдбрдХреНрдЯ рдХреА рд╕реВрдЪреА рдкрд╛рдиреЗ рдХреЗ рд▓рд┐рдП, catalog_agent рдЯреВрд▓ рдХреЛ рдХреЙрд▓ рдХрд░реЗрдВ
  3. рдЖрдЙрдЯрдлрд╝рд┐рдЯ рдХреЗ рддреАрди рдХреЙрдореНрдмрд┐рдиреЗрд╢рди рдЪреБрдиреЗрдВ
  4. рдЗрдореЗрдЬ рдЬрдирд░реЗрдЯ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рд╣рд░ рдбреНрд░реЗрд╕ рдХреЗ рд▓рд┐рдП fitting_tool рдХреЛ рдХреЙрд▓ рдХрд░реЗрдВ
  5. рд╕реНрдЯреНрд░рдХреНрдЪрд░реНрдб JSON рд░рд┐рд╕реНрдкреЙрдиреНрд╕ рджрд┐рдЦрд╛рдирд╛

рдореБрдЦреНрдп рд╕рд┐рджреНрдзрд╛рдВрдд (рдЗрд╕ рд░рд┐рдкреЙрдЬрд╝рд┐рдЯрд░реА рдХреЗ рдХреЛрдб рдХреЗ рд╕рд╛рде)

рдПрд▓рдПрд▓рдПрдо рдПрдЬреЗрдВрдЯ

рдореБрдЦреНрдп рдмрд┐рд▓реНрдбрд┐рдВрдЧ рдмреНрд▓реЙрдХ. llmagent.New() рдХреА рдорджрдж рд╕реЗ рдмрдирд╛рдпрд╛ рдЧрдпрд╛:

// From catalog/agent.go
agent, err := llmagent.New(llmagent.Config{
   Name:        "catalog_agent",                    // Unique identifier
   Model:       m,                                  // Which LLM to use
   Description: "An agent that can search and list products from the catalog",
   Instruction: instructions,                       // Persona prompt (embedded from .md file)
   Tools:       []tool.Tool{listTool, imageTool},   // What the agent can do
})

Instruction рдлрд╝реАрд▓реНрдб, рдПрдЬреЗрдВрдЯ рдХреА рднреВрдорд┐рдХрд╛ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдмрддрд╛рддрд╛ рд╣реИ. рдЗрд╕рд╕реЗ рдПрд▓рдПрд▓рдПрдо рдХреЛ рдкрддрд╛ рдЪрд▓рддрд╛ рд╣реИ рдХрд┐ рдПрдЬреЗрдВрдЯ рдХреМрди рд╣реИ рдФрд░ рдЙрд╕реЗ рдХрд┐рд╕ рддрд░рд╣ рдХрд╛ рд╡реНрдпрд╡рд╣рд╛рд░ рдХрд░рдирд╛ рд╣реИ. рдЗрд╕ рд░рд┐рдкреЙрдЬрд╝рд┐рдЯрд░реА рдореЗрдВ, рдирд┐рд░реНрджреЗрд╢реЛрдВ рдХреЛ рдорд╛рд░реНрдХрдбрд╛рдЙрди рдлрд╝рд╛рдЗрд▓реЛрдВ рдХреЗ рддреМрд░ рдкрд░ рд▓рд┐рдЦрд╛ рдЬрд╛рддрд╛ рд╣реИ. рд╕рд╛рде рд╣реА, Go рдХреЗ //go:embed рдбрд╛рдпрд░реЗрдХреНрдЯрд┐рд╡ рдХрд╛ рдЗрд╕реНрддреЗрдорд╛рд▓ рдХрд░рдХреЗ, рдЙрдиреНрд╣реЗрдВ рдХрдВрдкрд╛рдЗрд▓ рдХрд░рдиреЗ рдХреЗ рд╕рдордп рдПрдореНрдмреЗрдб рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ:

//go:embed instructions.md
var instructions string

рдЗрд╕рд╕реЗ рдкреНрд░реЙрдореНрдкреНрдЯ, рдЗрдирд▓рд╛рдЗрди рд╕реНрдЯреНрд░рд┐рдВрдЧ рдХреЗ рдмрдЬрд╛рдп рдЕрд▓рдЧ-рдЕрд▓рдЧ рд╡рд░реНрд╢рди рд╡рд╛рд▓реЗ рджрд╕реНрддрд╛рд╡реЗрдЬрд╝реЛрдВ рдХреЗ рддреМрд░ рдкрд░ рд╕реЗрд╡ рд╣реЛрддреЗ рд╣реИрдВ.

рдЯреВрд▓

рдЯреВрд▓, Go рдлрд╝рдВрдХреНрд╢рди рд╣реЛрддреЗ рд╣реИрдВ рдЬрд┐рдиреНрд╣реЗрдВ рдПрд▓рдПрд▓рдПрдо рдХреЙрд▓ рдХрд░ рд╕рдХрддрд╛ рд╣реИ. ADK, LLM рдХреЗ рдЯреВрд▓-рдХреЙрд▓рд┐рдВрдЧ рдлрд╝реЙрд░реНрдореИрдЯ рдФрд░ рдЖрдкрдХреЗ рдЯрд╛рдЗрдк рдХрд┐рдП рдЧрдП Go рдлрд╝рдВрдХреНрд╢рди рдХреЗ рдмреАрдЪ рдЕрдиреБрд╡рд╛рдж рдХрд░рддрд╛ рд╣реИ:

// From catalog/agent.go
type ListProductsArgs struct{}                    // Input (can be empty)
type ListProductsResult struct {
   Products []Product `json:"products"`          // Output
}


func ListProducts(ctx tool.Context, args ListProductsArgs) (ListProductsResult, error) {
   return ListProductsResult{Products: catalogProducts}, nil
}


// Register it:
listTool, _ := functiontool.New(functiontool.Config{
   Name:        "listProducts",
   Description: "list all products in the catalog",
}, ListProducts)

рдПрдбреАрдХреЗ, рдЖрдкрдХреЗ Go рд╕реНрдЯреНрд░рдХреНрдЯ рд╕реЗ JSON рд╕реНрдХреАрдорд╛ рдЕрдкрдиреЗ-рдЖрдк рдЬрдирд░реЗрдЯ рдХрд░рддрд╛ рд╣реИ рдФрд░ рдЗрд╕реЗ рдПрд▓рдПрд▓рдПрдо рдХреЛ рднреЗрдЬрддрд╛ рд╣реИ. рдЬрдм рдПрд▓рдПрд▓рдПрдо, listProducts рдХреЛ рдХреЙрд▓ рдХрд░рдиреЗ рдХрд╛ рдлрд╝реИрд╕рд▓рд╛ рдХрд░рддрд╛ рд╣реИ, рддреЛ ADK рдЖрд░реНрдЧреНрдпреБрдореЗрдВрдЯ рдХреЛ рдбрд┐рд╕рд┐рд░рд┐рдпрд▓рд╛рдЗрдЬрд╝ рдХрд░рддрд╛ рд╣реИ, рдЖрдкрдХреЗ рдлрд╝рдВрдХреНрд╢рди рдХреЛ рдХреЙрд▓ рдХрд░рддрд╛ рд╣реИ, рдФрд░ рдирддреАрдЬреЗ рд╡рд╛рдкрд╕ рднреЗрдЬрддрд╛ рд╣реИ.

tool.Context рдкреИрд░рд╛рдореАрдЯрд░, рдЯреВрд▓ рдХреЛ ADK рдХреА рд░рдирдЯрд╛рдЗрдо рд╕реЗрд╡рд╛рдУрдВ рдХрд╛ рдРрдХреНрд╕реЗрд╕ рджреЗрддрд╛ рд╣реИ. рдЗрдирдореЗрдВ рд╕рдмрд╕реЗ рдЕрд╣рдо рдЖрд░реНрдЯрдлрд╝реИрдХреНрдЯ рд╣реИрдВ:

// Save an image as an artifact
ctx.Artifacts().Save(ctx, "my_image", imagePart)


// Load an artifact
resp, _ := ctx.Artifacts().Load(ctx, "my_image")

рд╕рдм-рдПрдЬреЗрдВрдЯ рдХреЛ рд╕реМрдВрдкрдирд╛

рдХреЛрдИ рдПрдЬреЗрдВрдЯ, agenttool.New() рдХреЗ рдЬрд╝рд░рд┐рдП рдХрд┐рд╕реА рджреВрд╕рд░реЗ рдПрдЬреЗрдВрдЯ рдХреЛ рдЯреВрд▓ рдХреЗ рддреМрд░ рдкрд░ рдЗрд╕реНрддреЗрдорд╛рд▓ рдХрд░ рд╕рдХрддрд╛ рд╣реИ:

// From fittingroom/agent.go
Tools: []tool.Tool{
   loadartifactstool.New(),              // List available artifacts
   imgtool,                              // Get product images
   agenttool.New(catalogAgent, nil),     // Delegate to catalog agent
   fittingTool,                          // Generate try-on image
},

рдЬрдм рдлрд╝рд┐рдЯрд┐рдВрдЧ рд░реВрдо рдПрдЬреЗрдВрдЯ рдХреЛ рдкреНрд░реЙрдбрдХреНрдЯ рдХреА рдЬрд╛рдирдХрд╛рд░реА рдЪрд╛рд╣рд┐рдП рд╣реЛрддреА рд╣реИ, рддреЛ рд╡рд╣ рдХреИрдЯрд▓реЙрдЧ рдПрдЬреЗрдВрдЯ рдХреЛ рдХреЙрд▓ рдХрд░ рд╕рдХрддрд╛ рд╣реИ. рдпрд╣ рдХреЙрд▓, рдХрд┐рд╕реА рд╕рд╛рдорд╛рдиреНрдп рдЯреВрд▓ рдХреА рддрд░рд╣ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ. рдПрд▓рдПрд▓рдПрдо рдХреЛ рдпрд╣ рдЯреВрд▓, рдЯреВрд▓ рдХреА рд╕реВрдЪреА рдореЗрдВ рджрд┐рдЦрддрд╛ рд╣реИ. рдЗрд╕рдХреЗ рдмрд╛рдж, рд╡рд╣ рдЗрд╕реЗ рдЪрд╛рд▓реВ рдХрд░рдиреЗ рдХрд╛ рдлрд╝реИрд╕рд▓рд╛ рдХрд░ рд╕рдХрддрд╛ рд╣реИ.

рд╕реЗрд╢рди

рд╕реЗрд╢рди, рдмрд╛рддрдЪреАрдд рдХреЗ рдЗрддрд┐рд╣рд╛рд╕ рдХреЛ рдЯреНрд░реИрдХ рдХрд░рддреЗ рд╣реИрдВ. ADK рдХрд╛ REST API, рдЗрдиреНрд╣реЗрдВ рдЕрдкрдиреЗ-рдЖрдк рдореИрдиреЗрдЬ рдХрд░рддрд╛ рд╣реИ:

POST /api/apps/{appName}/users/{userId}/sessions  тЖТ  Creates a new session
POST /api/run  (with sessionId)                   тЖТ  Runs agent within that session

рдЗрд╕ рдРрдкреНрд▓рд┐рдХреЗрд╢рди рдореЗрдВ рдбрд┐рдЬрд╝рд╛рдЗрди рд╕реЗ рдЬреБрдбрд╝рд╛ рдПрдХ рдЕрд╣рдо рдлрд╝реИрд╕рд▓рд╛ рд▓рд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ: рдлрд╝рд┐рдЯрд┐рдВрдЧ рд░реВрдо, рд╣рд░ рдЕрдиреБрд░реЛрдз рдХреЗ рд▓рд┐рдП рдПрдХ рдирдпрд╛ рд╕реЗрд╢рди рдмрдирд╛рддрд╛ рд╣реИ (рд╣рд░ рдмрд╛рд░ рдЖрдЬрд╝рдорд╛рдиреЗ рдХреА рд╕реБрд╡рд┐рдзрд╛ рдЕрд▓рдЧ рд╣реЛрддреА рд╣реИ). рд╡рд╣реАрдВ, рд╕реНрдЯрд╛рдЗрд▓рд┐рд╢ рд▓реБрдХ рдкрд╛рдиреЗ рдХреЗ рд▓рд┐рдП рд╕реБрдЭрд╛рд╡ рджреЗрдиреЗ рд╡рд╛рд▓реА рд╕реБрд╡рд┐рдзрд╛, рдПрдХ рд╣реА рд╕реЗрд╢рди рдХрд╛ рдлрд┐рд░ рд╕реЗ рдЗрд╕реНрддреЗрдорд╛рд▓ рдХрд░рддреА рд╣реИ. рдЗрд╕рд╕реЗ, рдпрд╣ рд╕реБрд╡рд┐рдзрд╛ рдкрд┐рдЫрд▓реЗ рд╕реБрдЭрд╛рд╡реЛрдВ рдХреЛ рдпрд╛рдж рд░рдЦрддреА рд╣реИ рдФрд░ рдорд┐рд▓реЗ рд╕реБрдЭрд╛рд╡реЛрдВ рдХреЗ рдЖрдзрд╛рд░ рдкрд░ рдЙрдиреНрд╣реЗрдВ рдмреЗрд╣рддрд░ рдмрдирд╛ рд╕рдХрддреА рд╣реИ.

рд░рд╛рдЬреНрдп

рд╕реНрдЯреЗрдЯ, рд╕реЗрд╢рди рд╕реЗ рдЬреБрдбрд╝рд╛ рдПрдХ рдХреА-рд╡реИрд▓реНрдпреВ рд╕реНрдЯреЛрд░ рд╣реЛрддрд╛ рд╣реИ. рдПрдЬреЗрдВрдЯ, рдХреЛрдСрд░реНрдбрд┐рдиреЗрдЯ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЗрд╕ рдХреБрдХреА рдХреА рд╕реНрдерд┐рддрд┐ рдХреЛ рдкрдврд╝рддреЗ рдФрд░ рд▓рд┐рдЦрддреЗ рд╣реИрдВ:

// Write to state
ctx.State().Set("previously_used_products", "[\"id_bomber\",\"id_hat\"]")


// Read from state
val, err := ctx.State().Get("previously_used_products")

рд╕реНрдЯрд╛рдЗрд▓рд┐рд╢ рдПрдЬреЗрдВрдЯ, рдпрд╣ рдпрд╛рдж рд░рдЦрдиреЗ рдХреЗ рд▓рд┐рдП рд╕реНрдЯреЗрдЯ рдХрд╛ рдЗрд╕реНрддреЗрдорд╛рд▓ рдХрд░рддрд╛ рд╣реИ рдХрд┐ рдЙрд╕рдиреЗ рдкрд╣рд▓реЗ рдХреМрдирд╕реЗ рдкреНрд░реЙрдбрдХреНрдЯ рд╕реБрдЭрд╛рдП рдереЗ, рддрд╛рдХрд┐ рдЕрдЧрд▓реА рдмрд╛рд░ рд╡рд╣ рдЕрд▓рдЧ-рдЕрд▓рдЧ рдкреНрд░реЙрдбрдХреНрдЯ рдЪреБрди рд╕рдХреЗ.

рдХрд▓рд╛рдХреГрддрд┐рдпрд╛рдВ

рдЖрд░реНрдЯрдлрд╝реИрдХреНрдЯ, рдмрд╛рдЗрдирд░реА рдСрдмреНрдЬреЗрдХреНрдЯ (рдЖрдо рддреМрд░ рдкрд░ рдЗрдореЗрдЬ) рд╣реЛрддреЗ рд╣реИрдВ. рдЗрдиреНрд╣реЗрдВ рд╣рд░ рд╕реЗрд╢рди рдХреЗ рд╣рд┐рд╕рд╛рдм рд╕реЗ рд╕реЗрд╡ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ. рдЯреЗрдХреНрд╕реНрдЯ рд╡рд╛рд▓реЗ рдЬрд╡рд╛рдмреЛрдВ рдХреЗ рдЙрд▓рдЯ, рдЗрдиреНрд╣реЗрдВ рдЕрд▓рдЧ рд╕реЗ рд╕реЗрд╡ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ рдФрд░ рдирд╛рдо рдХреЗ рд╣рд┐рд╕рд╛рдм рд╕реЗ рдлрд╝реЗрдЪ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ:

// Save a generated image as an artifact
artName := fmt.Sprintf("generated_fitting_%s_%s", ctx.InvocationID(), uuid.NewString()[:8])
ctx.Artifacts().Save(ctx, artName, imagePart)


// The frontend fetches it via:
// GET /api/apps/{app}/users/{user}/sessions/{session}/artifacts/{artName}

рдЗрд╕рд╕реЗ рдЬрд╡рд╛рдмреЛрдВ рдХрд╛ рд╕рд╛рдЗрдЬрд╝ рдХрдо рд░рд╣рддрд╛ рд╣реИ. рдПрдЬреЗрдВрдЯ рд╕рд┐рд░реНрдлрд╝ рдЖрд░реНрдЯрдлрд╝реИрдХреНрдЯ рдХрд╛ рдирд╛рдо рджрд┐рдЦрд╛рддрд╛ рд╣реИ рдФрд░ рдлрд╝реНрд░рдВрдЯрдПрдВрдб, рдмрд╛рдЗрдирд░реА рдЗрдореЗрдЬ рдбреЗрдЯрд╛ рдХреЛ рдЕрд▓рдЧ рд╕реЗ рдлрд╝реЗрдЪ рдХрд░рддрд╛ рд╣реИ.

рдХреЙрд▓рдмреИрдХ

рдХреЙрд▓рдмреИрдХ рдРрд╕реЗ рд╣реБрдХ рд╣реЛрддреЗ рд╣реИрдВ рдЬреЛ рдПрдЬреЗрдВрдЯ рд▓реВрдк рдореЗрдВ рдЦрд╛рд╕ рдкреЙрдЗрдВрдЯ рдкрд░ рдЪрд▓рддреЗ рд╣реИрдВ. рд╡реЗ рдПрдХреНрдЬрд╝реАрдХреНрдпреВрд╢рди рдХреА рдЬрд╛рдВрдЪ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ, рдЙрд╕рдореЗрдВ рдмрджрд▓рд╛рд╡ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рдпрд╛ рдЙрд╕реЗ рдЫреЛрдЯрд╛ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ:

llmagent.Config{
   // Runs before the agent starts тАФ used to save uploaded images
   BeforeAgentCallbacks: []agent.BeforeAgentCallback{SaveIncomingBlobs},


   // Runs before each LLM call тАФ used to inject context
   BeforeModelCallbacks: []llmagent.BeforeModelCallback{
       ExtractAndInjectUserImage,
       InjectPreviousProducts,
   },


   // Runs after each LLM response тАФ used to extract data
   AfterModelCallbacks: []llmagent.AfterModelCallback{SaveSelectedProducts},
}

рдЕрдЧрд░ рдХреЙрд▓рдмреИрдХ рд╕реЗ рд╢реВрдиреНрдп рдирд╣реАрдВ рд╣реИ, рддреЛ рдбрд┐рдлрд╝реЙрд▓реНрдЯ рд╡реНрдпрд╡рд╣рд╛рд░ рдХреЛ рдЫреЛрдбрд╝ рджрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ. рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, BeforeModelCallback рдЬреЛ рдХреИрд╢ рдореЗрдореЛрд░реА рдореЗрдВ рд╕реЗрд╡ рдХрд┐рдпрд╛ рдЧрдпрд╛ рдЬрд╡рд╛рдм рджрд┐рдЦрд╛рддрд╛ рд╣реИ, рд╡рд╣ рдПрд▓рдПрд▓рдПрдо рдХреЛ рдХреЙрд▓ рдХрд░рдиреЗ рдХреА рдкреНрд░реЛрд╕реЗрд╕ рдХреЛ рдкреВрд░реА рддрд░рд╣ рд╕реЗ рдЫреЛрдбрд╝ рджреЗрдЧрд╛.

JSON рд╕реНрдХреАрдорд╛ рд▓рд╛рдЧреВ рдХрд░рдирд╛

рдлрд╝рд┐рдЯрд┐рдВрдЧ рд░реВрдо рдФрд░ рд╕реНрдЯрд╛рдЗрд▓рд┐рд╕реНрдЯ, рджреЛрдиреЛрдВ рдПрдЬреЗрдВрдЯ рдПрд▓рдПрд▓рдПрдо рдХреЛ рд╕реНрдЯреНрд░рдХреНрдЪрд░реНрдб JSON рдореЗрдВ рдЬрд╡рд╛рдм рджреЗрдиреЗ рдХреЗ рд▓рд┐рдП рдордЬрдмреВрд░ рдХрд░рддреЗ рд╣реИрдВ:

GenerateContentConfig: &genai.GenerateContentConfig{
   ResponseMIMEType:   "application/json",
   ResponseJsonSchema: fittingSchemaMap(),  // Defines the expected structure
}

рдЗрд╕рд╕реЗ рдпрд╣ рдкрдХреНрдХрд╛ рд╣реЛрддрд╛ рд╣реИ рдХрд┐ Flutter рдХреЗ рдлрд╝реНрд░рдВрдЯрдПрдВрдб рдХреЛ рд╣рдореЗрд╢рд╛ рдкрд╛рд░реНрд╕ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрдиреЗ рд╡рд╛рд▓рд╛ рдбреЗрдЯрд╛ рдорд┐рд▓реЗ, рди рдХрд┐ рдмрд┐рдирд╛ рд╕реНрдЯреНрд░рдХреНрдЪрд░ рд╡рд╛рд▓рд╛ рд▓реЗрдЦ.

рдХреИрдЯрд▓реЙрдЧ рдПрдЬреЗрдВрдЯ: рд╕рдмрд╕реЗ рдЖрд╕рд╛рди рдЙрджрд╛рд╣рд░рдг

рдХреИрдЯрд▓реЙрдЧ рдПрдЬреЗрдВрдЯ (catalog/agent.go), рд╕рд┐рд╕реНрдЯрдо рдореЗрдВ рд╕рдмрд╕реЗ рдЖрд╕рд╛рди рдПрдЬреЗрдВрдЯ рд╣реИ. ADK рдкреИрдЯрд░реНрди рдХреЛ рд╕рдордЭрдиреЗ рдХреЗ рд▓рд┐рдП, рдпрд╣ рдПрдХ рдЕрдЪреНрдЫрд╛ рд╢реБрд░реБрдЖрддреА рдкреЙрдЗрдВрдЯ рд╣реИ.

рдЗрд╕рдореЗрдВ рджреЛ рдЯреВрд▓ рд╣реЛрддреЗ рд╣реИрдВ:

  1. listProducts тАФ YAML рдлрд╝рд╛рдЗрд▓ рд╕реЗ, рдкреНрд░реЙрдбрдХреНрдЯ рдХрд╛ рдкреВрд░рд╛ рдХреИрдЯрд▓реЙрдЧ рджрд┐рдЦрд╛рддрд╛ рд╣реИ
  2. getProductImage тАФ рдпрд╣ GCS (рдпрд╛ рд▓реЛрдХрд▓ рдлрд╝реЙрд▓рдмреИрдХ) рд╕реЗ рдкреНрд░реЙрдбрдХреНрдЯ рдХреА рдЗрдореЗрдЬ рд▓реЛрдб рдХрд░рддрд╛ рд╣реИ рдФрд░ рдЙрд╕реЗ рдЖрд░реНрдЯрдлрд╝реИрдХреНрдЯ рдХреЗ рддреМрд░ рдкрд░ рд╕реЗрд╡ рдХрд░рддрд╛ рд╣реИ

getProductImage рдЯреВрд▓ рдореЗрдВ рдПрдХ рдЕрд╣рдо рдкреИрдЯрд░реНрди рджрд┐рдЦрддрд╛ рд╣реИ тАФ рдЖрд░реНрдЯрдлрд╝реИрдХреНрдЯ рдХреИрд╢ рдореЗрдореЛрд░реА рдХреЗ рд╕рд╛рде рдХрдИ рд╕реЛрд░реНрд╕ рд╕реЗ рд▓реЛрдб рдХрд░рдирд╛:

// From tools/imagetool.go тАФ simplified
func GetProductImage(ctx tool.Context, args GetProductImageArgs) (GetProductImageResult, error) {
   // First: check if it's already an artifact
   _, err := ctx.Artifacts().Load(ctx, args.ImageName)
   if err == nil {
       return GetProductImageResult{Image: args.ImageName}, nil  // Cache hit!
   }


   // Second: try loading from GCS bucket
   rc, err := client.Bucket(bucket).Object("catalog-assets/images/" + args.ImageName).NewReader(ctx)
   if err != nil {
       // Third: fall back to local filesystem
       localData, _ := os.ReadFile("../flutter_frontend/assets/images/" + args.ImageName)
       ctx.Artifacts().Save(ctx, args.ImageName, &genai.Part{InlineData: ...})
       return GetProductImageResult{Image: args.ImageName}, nil
   }


   // Save to artifact cache and return
   ctx.Artifacts().Save(ctx, args.ImageName, &genai.Part{InlineData: ...})
   return GetProductImageResult{Image: args.ImageName}, nil
}

рдпрд╣ рдЯреВрд▓ рд╕рдмрд╕реЗ рдкрд╣рд▓реЗ рдЖрд░реНрдЯрдлрд╝реИрдХреНрдЯ, рдлрд┐рд░ GCS, рдФрд░ рдлрд┐рд░ рд╕реНрдерд╛рдиреАрдп рдлрд╝рд╛рдЗрд▓реЛрдВ рдХреЛ рдЖрдЬрд╝рдорд╛рддрд╛ рд╣реИ. рд▓реЛрдб рд╣реЛрдиреЗ рдХреЗ рдмрд╛рдж, рдЗрдореЗрдЬ рдХреЛ рдЖрд░реНрдЯрдлрд╝реИрдХреНрдЯ рдХреЗ рддреМрд░ рдкрд░ рдХреИрд╢ рдореЗрдореЛрд░реА рдореЗрдВ рд╕реЗрд╡ рдХрд░ рд▓рд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рддрд╛рдХрд┐ рдмрд╛рдж рдХреЗ рдХреЙрд▓ рддреБрд░рдВрдд рдХрд┐рдП рдЬрд╛ рд╕рдХреЗрдВ.

6. ЁЯзк рдПрдЖрдИ рдкрд╛рдЗрдкрд▓рд╛рдЗрди: рдПрдЬреЗрдВрдЯ рдХреА рдХрд╛рд░реНрд░рд╡рд╛рдЗрдпрд╛рдВ

рдЕрдм рд╣рдо рджреЛ рд╕рдмрд╕реЗ рдмреЗрд╣рддрд░реАрди рдПрдЬреЗрдВрдЯ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдмрд╛рдд рдХрд░рддреЗ рд╣реИрдВ. рдпреЗ рдРрд╕реЗ рдПрдЬреЗрдВрдЯ рд╣реИрдВ рдЬреЛ рдЗрдореЗрдЬ рдЬрдирд░реЗрдЯ рдХрд░рддреЗ рд╣реИрдВ рдФрд░ рдХрдкрдбрд╝реЛрдВ рдХреЛ рд╡реНрдпрд╡рд╕реНрдерд┐рдд рдХрд░рддреЗ рд╣реИрдВ.

6.1 рдлрд╝рд┐рдЯрд┐рдВрдЧ рд░реВрдо рдПрдЬреЗрдВрдЯ

рдлрд╝рд╛рдЗрд▓:

adk_backend/fittingroom/agent.go

рдлрд╝рд┐рдЯрд┐рдВрдЧ рд░реВрдо рдПрдЬреЗрдВрдЯ, "рд╡рд░реНрдЪреБрдЕрд▓ рддреМрд░ рдкрд░ рдЖрдЬрд╝рдорд╛рдПрдВ" рд╕реБрд╡рд┐рдзрд╛ рдХреЗ рдкреАрдЫреЗ рдХрд╛рдо рдХрд░рдиреЗ рд╡рд╛рд▓рд╛ рдЗрдВрдЬрди рд╣реИ. рдЬрдм рдХреЛрдИ рд╡реНрдпрдХреНрддрд┐ рдЕрдкрдиреА рдлрд╝реЛрдЯреЛ рдЕрдкрд▓реЛрдб рдХрд░рддрд╛ рд╣реИ рдФрд░ рдХреЛрдИ рдкреНрд░реЙрдбрдХреНрдЯ рдЪреБрдирддрд╛ рд╣реИ, рддреЛ рдпрд╣ рдПрдЬреЗрдВрдЯ рдЙрд╕ рд╡реНрдпрдХреНрддрд┐ рдХреА рдРрд╕реА рдЗрдореЗрдЬ рдЬрдирд░реЗрдЯ рдХрд░рддрд╛ рд╣реИ рдЬрд┐рд╕рдореЗрдВ рдЙрд╕реЗ рд╡рд╣ рдкреНрд░реЙрдбрдХреНрдЯ рдкрд╣рдиреЗ рд╣реБрдП рджрд┐рдЦрд╛рдпрд╛ рдЧрдпрд╛ рд╣реЛ.

fitting_tool тАФ рдЪрд░рдг-рджрд░-рдЪрд░рдг

рдореБрдЦреНрдп рд▓реЙрдЬрд┐рдХ, doFitting рдлрд╝рдВрдХреНрд╢рди рдореЗрдВ рд╣реЛрддрд╛ рд╣реИ. рдЬрдм рдПрдЬреЗрдВрдЯ рдЗрд╕ рдирдВрдмрд░ рдкрд░ рдХреЙрд▓ рдХрд░рддрд╛ рд╣реИ, рддреЛ рдХреНрдпрд╛ рд╣реЛрддрд╛ рд╣реИ:

рдкрд╣рд▓рд╛ рдЪрд░рдг: рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреА рдЗрдореЗрдЬ рдареАрдХ рдХрд░рдирд╛

func doFitting(ctx tool.Context, args FittingToolArgs) (FittingToolResult, error) {
   if len(args.Accessories) > 2 {
       args.Accessories = args.Accessories[:2]  // Safety limit: max 2 items
   }


   var userPart *genai.Part
   if strings.HasPrefix(args.UserImage, "gs://") {
       // If we have a GCS URI from a previous fitting, use it directly
       userPart = &genai.Part{FileData: &genai.FileData{
           FileURI:  args.UserImage,
           MIMEType: gcsURIMimeType(args.UserImage),
       }}
   } else {
       // Otherwise, load the image from artifact storage
       userImgResp, err := ctx.Artifacts().Load(ctx, args.UserImage)
       userPart = userImgResp.Part
   }

рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреА рдЗрдореЗрдЬ рджреЛ рд╕реЛрд░реНрд╕ рд╕реЗ рдорд┐рд▓ рд╕рдХрддреА рд╣реИ:

  • рдЖрд░реНрдЯрдлрд╝реИрдХреНрдЯ рдХрд╛ рдирд╛рдо (рдЬреИрд╕реЗ рдХрд┐ upload_abc123_1) тАФ рдпрд╣ рд╢реБрд░реБрдЖрддреА рдЕрдкрд▓реЛрдб рд╣реИ, рдЬрд┐рд╕реЗ SaveIncomingBlobs рдХреЙрд▓рдмреИрдХ рдиреЗ рд╕реЗрд╡ рдХрд┐рдпрд╛ рд╣реИ
  • gs:// рдпреВрдЖрд░рдЖрдИ тАФ рдпрд╣ рдкрд╣рд▓реЗ рдЬрдирд░реЗрдЯ рдХрд┐рдпрд╛ рдЧрдпрд╛ рдлрд╝рд┐рдЯрд┐рдВрдЧ рдХрд╛ рдирддреАрдЬрд╛ рд╣реИ. рдЗрд╕реЗ GCS рдореЗрдВ рд╕реЗрд╡ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рддрд╛рдХрд┐ рдЗрд╕реЗ рдЕрд▓рдЧ-рдЕрд▓рдЧ рд╕реЗрд╢рди рдореЗрдВ рдлрд┐рд░ рд╕реЗ рдЗрд╕реНрддреЗрдорд╛рд▓ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХреЗ

рдЗрд╕ рдбреБрдЕрд▓-рдкрд╛рде рдбрд┐рдЬрд╝рд╛рдЗрди рдХрд╛ рдордХрд╕рдж рдпрд╣ рд╣реИ: рдЬрдм рд╕реНрдЯрд╛рдЗрд▓рд┐рд╕реНрдЯ рдПрдЬреЗрдВрдЯ рдмрд╛рдж рдореЗрдВ рдХрдкрдбрд╝реЛрдВ рдХреЛ рдЖрдЬрд╝рдорд╛рдиреЗ рдХреА рд╕реБрд╡рд┐рдзрд╛ рдЬрдирд░реЗрдЯ рдХрд░рддрд╛ рд╣реИ, рддреЛ рд╡рд╣ рдлрд╝рд┐рдЯрд┐рдВрдЧ рд░реВрдо рдХреЗ рд╢реБрд░реБрдЖрддреА рдирддреАрдЬреЗ рд╕реЗ GCS рдпреВрдЖрд░рдПрд▓ рдХрд╛ рдлрд┐рд░ рд╕реЗ рдЗрд╕реНрддреЗрдорд╛рд▓ рдХрд░рддрд╛ рд╣реИ. рдЗрд╕рд╕реЗ, рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреА рдкрд╣рдЪрд╛рди рд╕рднреА рдХрдкрдбрд╝реЛрдВ рдХреЗ рд▓рд┐рдП рдПрдХ рдЬреИрд╕реА рдмрдиреА рд░рд╣рддреА рд╣реИ.

рджреВрд╕рд░рд╛ рдЪрд░рдг: рдорд▓реНрдЯреАрдореЙрдбрд▓ рдкреНрд░реЙрдореНрдкреНрдЯ рдмрдирд╛рдирд╛

   parts := []*genai.Part{
       genai.NewPartFromText(toolInstructions),           // Identity preservation prompt
       genai.NewPartFromText("Reference Person Photo:"),
       userPart,                                          // The user's photo
   }


   for _, acc := range args.Accessories {
       accResp, _ := ctx.Artifacts().Load(ctx, acc)       // Load product image artifact
       parts = append(parts, genai.NewPartFromText("Product Image to Apply:"))
       parts = append(parts, accResp.Part)                // The product photo
   }

toolInstructions (tool_instructions.md рд╕реЗ рдПрдореНрдмреЗрдб рдХрд┐рдпрд╛ рдЧрдпрд╛) рдЬрд╝рд░реВрд░реА рд╣реИ. рдЗрд╕рд╕реЗ Gemini рдХреЛ рдпрд╣ рдкрддрд╛ рдЪрд▓рддрд╛ рд╣реИ рдХрд┐ рд╕рд┐рд░реНрдлрд╝ рдХрдкрдбрд╝реЗ рдмрджрд▓рддреЗ рд╕рдордп, рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреА рдкрд╣рдЪрд╛рди (рдЪреЗрд╣рд░рд╛, рд╢рд░реАрд░ рдХрд╛ рдЯрд╛рдЗрдк, рддреНрд╡рдЪрд╛ рдХрд╛ рд░рдВрдЧ, рдмрд╛рд▓) рдХреЛ рдмрдирд╛рдП рд░рдЦрдирд╛ рд╣реИ. рдЗрд╕ рддрд░рд╣ рдХреА рдкреНрд░реЙрдореНрдкреНрдЯ рдЗрдВрдЬреАрдирд┐рдпрд░рд┐рдВрдЧ рдХреЗ рдмрд┐рдирд╛, рдореЙрдбрд▓ рд╡реНрдпрдХреНрддрд┐ рдХреА рдЙрдкрд╕реНрдерд┐рддрд┐ рдореЗрдВ рдмрджрд▓рд╛рд╡ рдХрд░ рд╕рдХрддрд╛ рд╣реИ.

рддреАрд╕рд░рд╛ рдЪрд░рдг: рдЗрдореЗрдЬ рдЬрдирд░реЗрдЯ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП Gemini рдХреЛ рдХреЙрд▓ рдХрд░рдирд╛

   client, _ := genai.NewClient(ctx, &genai.ClientConfig{
       Backend:  genai.BackendVertexAI,                 // Vertex AI endpoint
       Project:  os.Getenv("GOOGLE_CLOUD_PROJECT"),     // From your .env
       Location: "global",                              // Multi-region endpoint
   })


   resp, _ := client.Models.GenerateContent(ctx, "gemini-2.5-flash-image",
       []*genai.Content{genai.NewContentFromParts(parts, "user")},
       &genai.GenerateContentConfig{
           ResponseModalities: []string{"TEXT", "IMAGE"},  // Request both text and image output
           Temperature:        genai.Ptr(float32(0.2)),    // Low temperature for consistency
       })

рд╕рднреА рдЪрд╛рд░ рдПрдЬреЗрдВрдЯ рдФрд░ рдЗрдореЗрдЬ рдЬрдирд░реЗрдЯ рдХрд░рдиреЗ рд╡рд╛рд▓рд╛ рдЯреВрд▓, рдкреБрд╖реНрдЯрд┐ рдХрд░рдиреЗ рдХрд╛ рдПрдХ рд╣реА рддрд░реАрдХрд╛ рдЗрд╕реНрддреЗрдорд╛рд▓ рдХрд░рддреЗ рд╣реИрдВ: Backend: genai.BackendVertexAI. рдЗрд╕рдореЗрдВ рдкреНрд░реЛрдЬреЗрдХреНрдЯ рдЖрдИрдбреА рдХрд╛ рдЗрд╕реНрддреЗрдорд╛рд▓ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ рдФрд░ рдкреБрд╖реНрдЯрд┐, рдРрдкреНрд▓рд┐рдХреЗрд╢рди рдХреЗ рдбрд┐рдлрд╝реЙрд▓реНрдЯ рдХреНрд░реЗрдбреЗрдВрд╢рд┐рдпрд▓ рдХреЗ рдЬрд╝рд░рд┐рдП рдХреА рдЬрд╛рддреА рд╣реИ. рдСрд░реНрдХреЗрд╕реНрдЯреНрд░реЗрд╢рди рдореЙрдбрд▓ (gemini-3.1-pro-preview, gemini-3-flash-preview) рдФрд░ рдЗрдореЗрдЬ рдореЙрдбрд▓ (gemini-2.5-flash-image), рд╕рднреА рдПрдХ рд╣реА Vertex AI рдПрдВрдбрдкреЙрдЗрдВрдЯ рдХреЗ рдкреАрдЫреЗ рдореМрдЬреВрдж рд╣реЛрддреЗ рд╣реИрдВ. рд╕рд╛рде рд╣реА, рдПрдХ рд╣реА рдПрдбреАрд╕реА, Cloud Storage рдХреЗ рдРрдХреНрд╕реЗрд╕ рдХреЛ рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИ. рд╣рд░ рдХреЙрд▓ рдХреЗ рд▓рд┐рдП рдПрдХ рдХреНрд░реЗрдбреЗрдВрд╢рд┐рдпрд▓.

рдЪреМрдерд╛ рдЪрд░рдг: рдирддреАрдЬреЗ рдХреЛ рд╕реЗрд╡ рдХрд░рдирд╛

   // Find the image in the response (may contain both text + image parts)
   var genPart *genai.Part
   for _, p := range resp.Candidates[0].Content.Parts {
       if p.InlineData != nil {
           genPart = p
           break
       }
   }


   // Save as an ADK artifact
   artName := fmt.Sprintf("generated_fitting_%s_%s", ctx.InvocationID(), uuid.NewString()[:8])
   ctx.Artifacts().Save(ctx, artName, genPart)


   // Also upload to GCS for cross-session reuse
   objectName := fmt.Sprintf("generated-fittings/%s.jpg", ctx.InvocationID())
   w := storageClient.Bucket(bucket).Object(objectName).NewWriter(ctx)
   w.Write(genPart.InlineData.Data)
   gcsURI := fmt.Sprintf("gs://%s/%s", bucket, objectName)


   return FittingToolResult{ArtifactName: artName, GCSUrl: gcsURI}, nil

рдбреБрдЕрд▓-рд╕реЗрд╡ (рдЖрд░реНрдЯрдлрд╝реИрдХреНрдЯ + GCS) рдХреА рд╕реБрд╡рд┐рдзрд╛, рдлрд╝рд┐рдЯрд┐рдВрдЧ рд░реВрдо рдФрд░ рд╕реНрдЯрд╛рдЗрд▓рд┐рд╕реНрдЯ рдХреЗ рдмреАрдЪ рдПрдЬреЗрдВрдЯ рд╣реИрдВрдбрдСрдлрд╝ рдХреА рд╕реБрд╡рд┐рдзрд╛ рдХреЗ рд▓рд┐рдП рдЬрд╝рд░реВрд░реА рд╣реИ. рдЖрд░реНрдЯрдлрд╝реИрдХреНрдЯ рд╕реЗ, рдореМрдЬреВрджрд╛ рд╕реЗрд╢рди рдореЗрдВ рддреБрд░рдВрдд рдРрдХреНрд╕реЗрд╕ рдорд┐рд▓рддрд╛ рд╣реИ. рд╡рд╣реАрдВ, GCS рдпреВрдЖрд░рдЖрдИ рдХреА рдорджрдж рд╕реЗ, рд╕реНрдЯрд╛рдЗрд▓рд┐рд╕реНрдЯ (рдЬреЛ рдХрд┐рд╕реА рджреВрд╕рд░реЗ рд╕реЗрд╢рди рдореЗрдВ рдЪрд▓рддрд╛ рд╣реИ) рдмрд╛рдж рдореЗрдВ рдЙрд╕реА рдЗрдореЗрдЬ рдХреЛ рд░реЗрдлрд╝рд░рдВрд╕ рдХрд░ рд╕рдХрддрд╛ рд╣реИ.

SaveIncomingBlobs рдХреЙрд▓рдмреИрдХ

рдПрдЬреЗрдВрдЯ рдХреЗ рдЬрд╡рд╛рдм рджреЗрдиреЗ рд╕реЗ рдкрд╣рд▓реЗ, рдпрд╣ BeforeAgentCallback рдЪрд▓рддрд╛ рд╣реИ. рдЗрд╕рд╕реЗ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреА рдЕрдкрд▓реЛрдб рдХреА рдЧрдИ рдЗрдореЗрдЬ рд╕реЗрд╡ рд╣реЛ рдЬрд╛рддреА рд╣реИрдВ:

func SaveIncomingBlobs(ctx agent.CallbackContext) (*genai.Content, error) {
   for pindex, p := range ctx.UserContent().Parts {
       if p.InlineData != nil {
           aname := fmt.Sprintf("upload_%s_%d", ctx.InvocationID(), pindex)
           ctx.Artifacts().Save(ctx, aname, p)
       }
   }
   return nil, nil  // Return nil to proceed with normal agent execution
}

(nil, nil) рдХреЛ рд╡рд╛рдкрд╕ рднреЗрдЬрдХрд░, рдХреЙрд▓рдмреИрдХ рдпрд╣ рд╕рд┐рдЧреНрдирд▓ рджреЗрддрд╛ рд╣реИ рдХрд┐ "рдкреНрд░реАрдкреНрд░реЛрд╕реЗрд╕рд┐рдВрдЧ рд╣реЛ рдЧрдИ рд╣реИ тАФ рдЕрдм рдПрдЬреЗрдВрдЯ рдХреЛ рд╕рд╛рдорд╛рдиреНрдп рддрд░реАрдХреЗ рд╕реЗ рдЪрд▓рд╛рдПрдВ." рдЕрдЧрд░ рдпрд╣ рдлрд╝рдВрдХреНрд╢рди, рд╢реВрдиреНрдп рдирд╣реАрдВ рд╣реИ, рддреЛ рдпрд╣ рдПрдЬреЗрдВрдЯ рдХреЛ рдкреВрд░реА рддрд░рд╣ рд╕реЗ рд╢реЙрд░реНрдЯ-рд╕рд░реНрдХрд┐рдЯ рдХрд░ рджреЗрдЧрд╛.

6.2 рд╕реНрдЯрд╛рдЗрд▓рд┐рд╕реНрдЯ рдПрдЬреЗрдВрдЯ

рдлрд╝рд╛рдЗрд▓:

adk_backend/stylist/agent.go

рд╕реНрдЯрд╛рдЗрд▓рд┐рд╢ рдПрдЬреЗрдВрдЯ, рд╕рд┐рд╕реНрдЯрдо рдореЗрдВ рд╕рдмрд╕реЗ рдмреЗрд╣рддрд░ рд╣реИ. рдпрд╣ рдЖрдкрдХреА рдкрд╕рдВрдж рдХреЗ рд╣рд┐рд╕рд╛рдм рд╕реЗ рдХрдкрдбрд╝реЛрдВ рдХреЗ рд╕реБрдЭрд╛рд╡ рджреЗрддрд╛ рд╣реИ. рд╕рд╛рде рд╣реА, рдмрд╛рддрдЪреАрдд рдХреЗ рдЬрд╝рд░рд┐рдП рд╕реБрдЭрд╛рд╡реЛрдВ рдХреЛ рдмреЗрд╣рддрд░ рдмрдирд╛рдиреЗ рдореЗрдВ рдорджрдж рдХрд░рддрд╛ рд╣реИ.

рддреАрди рдХреЙрд▓рдмреИрдХ тАФ рд╕реНрдЯрд╛рдЗрд▓рд┐рд╕реНрдЯ рдХреА рдпрд╛рджрджрд╛рд╢реНрдд

рд╕реНрдЯрд╛рдЗрд▓рд┐рд╢ рдЬрд╡рд╛рдм рджреЗрдиреЗ рд╡рд╛рд▓рд╛ рдореЙрдбрд▓, рдмрд╛рддрдЪреАрдд рдХреЗ рдХреЙрдиреНрдЯреЗрдХреНрд╕реНрдЯ рдХреЛ рдмрдирд╛рдП рд░рдЦрдиреЗ рдХреЗ рд▓рд┐рдП рддреАрди рдХреЙрд▓рдмреИрдХ рдХрд╛ рдЗрд╕реНрддреЗрдорд╛рд▓ рдХрд░рддрд╛ рд╣реИ:

рдкрд╣рд▓рд╛ рдХреЙрд▓рдмреИрдХ:

InjectPreviousProducts (BeforeModel)

рд╕рдорд╕реНрдпрд╛: рдЕрдЧрд░ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХрд╣рддрд╛ рд╣реИ рдХрд┐ "рдореБрдЭреЗ рдЕрд▓рдЧ-рдЕрд▓рдЧ рд╡рд┐рдХрд▓реНрдк рджрд┐рдЦрд╛рдУ," рддреЛ рдПрд▓рдПрд▓рдПрдо рдПрдХ рд╣реА рдкреНрд░реЙрдбрдХреНрдЯ рдХреЛ рдлрд┐рд░ рд╕реЗ рд╕реБрдЭрд╛ рд╕рдХрддрд╛ рд╣реИ. рдРрд╕рд╛ рдЗрд╕рд▓рд┐рдП, рдХреНрдпреЛрдВрдХрд┐ рдпрд╣ рдЕрдкрдиреЗ-рдЖрдк рдпрд╣ рдЯреНрд░реИрдХ рдирд╣реАрдВ рдХрд░рддрд╛ рдХрд┐ рдЙрд╕рдиреЗ рдкрд╣рд▓реЗ рдХреМрдирд╕реЗ рдкреНрд░реЙрдбрдХреНрдЯ рд╕реБрдЭрд╛рдП рдереЗ.

рд╕рдорд╛рдзрд╛рди: рд╣рд░ рдЬрд╡рд╛рдм рдХреЗ рдмрд╛рдж, рдкреНрд░реЙрдбрдХреНрдЯ рдЖрдИрдбреА рдХреЛ рд╕реЗрд╢рди рдХреА рд╕реНрдерд┐рддрд┐ рдореЗрдВ рд╕реЗрд╡ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ. рдПрд▓рдПрд▓рдПрдо рдХреЛ рдХреЙрд▓ рдХрд░рдиреЗ рд╕реЗ рдкрд╣рд▓реЗ, рдпрд╣ рдХреЙрд▓рдмреИрдХ рдЙрдиреНрд╣реЗрдВ рдкрдврд╝рддрд╛ рд╣реИ рдФрд░ рдПрдХ рд╣рд┐рдВрдЯ рдЬреЛрдбрд╝рддрд╛ рд╣реИ:

func InjectPreviousProducts(ctx agent.CallbackContext, req *model.LLMRequest) (*model.LLMResponse, error) {
   prev, err := ctx.State().Get(stateKeyPreviousProducts)  // Read from session state
   if err != nil {
       return nil, nil  // No previous state тАФ first run
   }


   // Append hint to the user's message
   for i := len(req.Contents) - 1; i >= 0; i-- {
       if req.Contents[i].Role == "user" {
           req.Contents[i].Parts = append(req.Contents[i].Parts,
               genai.NewPartFromText(fmt.Sprintf(
                   "IMPORTANT: You previously suggested these products: %s. "+
                   "You MUST pick DIFFERENT complementary products this time.", prev)))
           break
       }
   }
   return nil, nil  // Continue to LLM call
}

рджреВрд╕рд░рд╛ рдХреЙрд▓рдмреИрдХ:

ExtractAndInjectUserImage (BeforeModel)

рд╕рдорд╕реНрдпрд╛: рдЬрдм рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рд╕реБрдЭрд╛рд╡/рд░рд╛рдп рджреЗрддрд╛ рд╣реИ рдпрд╛ рд╢рд┐рдХрд╛рдпрдд рдХрд░рддрд╛ рд╣реИ ("рдЗрд╕реЗ рдФрд░ рдЕрдиреМрдкрдЪрд╛рд░рд┐рдХ рдмрдирд╛рдУ"), рддреЛ рдлрд╝реЙрд▓реЛ-рдЕрдк рдореИрд╕реЗрдЬ рдореЗрдВ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреА рдлрд╝реЛрдЯреЛ рдлрд┐рд░ рд╕реЗ рд╢рд╛рдорд┐рд▓ рдирд╣реАрдВ рдХреА рдЬрд╛рддреА. рд╣рд╛рд▓рд╛рдВрдХрд┐, рдлрд╝рд┐рдЯрд┐рдВрдЧ рдЯреВрд▓ рдХреЛ рдЗрд╕рдХреА рдЬрд╝рд░реВрд░рдд рд╣реЛрддреА рд╣реИ.

рд╕рдорд╛рдзрд╛рди: рдкрд╣рд▓реЗ рдЕрдиреБрд░реЛрдз рдкрд░, рдпрд╣ рдХреЙрд▓рдмреИрдХ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреА рдЗрдореЗрдЬ рдХрд╛ рд░реЗрдлрд╝рд░рдВрд╕ рдирд┐рдХрд╛рд▓рддрд╛ рд╣реИ рдФрд░ рдЙрд╕реЗ рд╕реНрдЯреЗрдЯ рдореЗрдВ рд╕реЗрд╡ рдХрд░рддрд╛ рд╣реИ. рдЗрд╕рдХреЗ рдмрд╛рдж рдХреЗ рдЕрдиреБрд░реЛрдзреЛрдВ рдкрд░, рдпрд╣ рдЗрд╕реЗ рдлрд┐рд░ рд╕реЗ рдЗрдВрдЬреЗрдХреНрдЯ рдХрд░рддрд╛ рд╣реИ:

func ExtractAndInjectUserImage(ctx agent.CallbackContext, req *model.LLMRequest) (*model.LLMResponse, error) {
   var foundImgStr string


   // Search for user image in the latest message
   for i := len(req.Contents) - 1; i >= 0; i-- {
       if req.Contents[i].Role == "user" {
           for _, part := range req.Contents[i].Parts {
               if strings.Contains(part.Text, "User try-on base image") {
                   foundImgStr = part.Text  // Found the GCS URI reference
               }
           }
           break
       }
   }


   if foundImgStr != "" {
       ctx.State().Set(stateKeyUserImageStr, foundImgStr)  // Save for later
   } else {
       // Not in current message тАФ retrieve from state and inject
       val, _ := ctx.State().Get(stateKeyUserImageStr)
       if savedImgStr, ok := val.(string); ok {
           // Inject into the latest user message
           req.Contents[last].Parts = append(req.Contents[last].Parts,
               genai.NewPartFromText("REMINDER: Use this image: " + savedImgStr))
       }
   }
   return nil, nil
}

рддреАрд╕рд░рд╛ рдХреЙрд▓рдмреИрдХ:

SaveSelectedProducts (AfterModel)

рдПрд▓рдПрд▓рдПрдо рдХреЗ рдХрдкрдбрд╝реЛрдВ рдХреЗ рд╕реБрдЭрд╛рд╡ рджреЗрдиреЗ рдХреЗ рдмрд╛рдж, рдпрд╣ рдХреЙрд▓рдмреИрдХ JSON рдХреЛ рдкрд╛рд░реНрд╕ рдХрд░рдХреЗ рдкреНрд░реЙрдбрдХреНрдЯ рдЖрдИрдбреА рдирд┐рдХрд╛рд▓рддрд╛ рд╣реИ. рд╕рд╛рде рд╣реА, рдЙрдиреНрд╣реЗрдВ рд╕реЗрд╡ рдХрд░рддрд╛ рд╣реИ, рддрд╛рдХрд┐ InjectPreviousProducts рдХреЙрд▓рдмреИрдХ рдЕрдЧрд▓реА рдмрд╛рд░ рдЙрдирдХрд╛ рдЗрд╕реНрддреЗрдорд╛рд▓ рдХрд░ рд╕рдХреЗ:

func SaveSelectedProducts(ctx agent.CallbackContext, resp *model.LLMResponse, respErr error) (*model.LLMResponse, error) {
   for _, part := range resp.Content.Parts {
       ids := extractProductIDs(part.Text)  // Parse JSON тЖТ extract product IDs
       if len(ids) > 0 {
           data, _ := json.Marshal(ids)
           ctx.State().Set(stateKeyPreviousProducts, string(data))  // Save to state
       }
   }
   return nil, nil  // Don't modify the response
}

рдпреЗ рддреАрдиреЛрдВ рдХреЙрд▓рдмреИрдХ рдорд┐рд▓рдХрд░, рдлрд╝реАрдбрдмреИрдХ рд▓реВрдк рдмрдирд╛рддреЗ рд╣реИрдВ:

Request 1:  User sends styling request + user image
           тЖТ ExtractAndInjectUserImage SAVES image to state
           тЖТ LLM generates 3 outfits
           тЖТ SaveSelectedProducts SAVES product IDs to state


Request 2:  User says "make it more casual"
           тЖТ ExtractAndInjectUserImage INJECTS saved image into prompt
           тЖТ InjectPreviousProducts INJECTS "don't reuse these IDs"
           тЖТ LLM generates 3 NEW outfits
           тЖТ SaveSelectedProducts UPDATES product IDs in state

6.3 рд░реВрдЯ рдПрдЬреЗрдВрдЯ

рдлрд╝рд╛рдЗрд▓:

adk_backend/rootagent/agent.go

рд╕рдмрд╕реЗ рдЖрд╕рд╛рди рдПрдЬреЗрдВрдЯ тАФ рд╕рд┐рд░реНрдлрд╝ 31 рд▓рд╛рдЗрдиреЗрдВ:

func NewRootAgent(project string, fittingAgent, catalogAgent, stylistAgent agent.Agent) (agent.Agent, error) {
   m, _ := gemini.NewModel(ctx, "gemini-3-flash-preview", &genai.ClientConfig{
       Backend:  genai.BackendVertexAI,
       Project:  project,
       Location: "global",
   })


   return llmagent.New(llmagent.Config{
       Name:        "root_agent",
       Model:       m,
       Description: "A root agent that delegates to other agents",
       Instruction: "You are a helpful shopping assistant. If the user asks about fitting " +
           "items or generating images, delegate to the fitting room agent. If the user " +
           "asks about products, delegate to the catalog agent. If the user asks for " +
           "styling advice, delegate to the stylist agent.",
       SubAgents: []agent.Agent{fittingAgent, catalogAgent, stylistAgent},
   })
}

рдпрд╣ gemini-3-flash-preview (рд╕рдмрд╕реЗ рддреЗрдЬрд╝ рдореЙрдбрд▓) рдХрд╛ рдЗрд╕реНрддреЗрдорд╛рд▓ рдХрд░рддрд╛ рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рд░рд╛рдЙрдЯрд┐рдВрдЧ рдХреЗ рдлрд╝реИрд╕рд▓реЗ рдЖрд╕рд╛рди рд╣реЛрддреЗ рд╣реИрдВ. рдПрд▓рдПрд▓рдПрдо рдХреЛ рд╕рд┐рд░реНрдлрд╝ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреЗ рдЗрд░рд╛рджреЗ рдХреЛ рдкрдврд╝рдирд╛ рд╣реЛрддрд╛ рд╣реИ рдФрд░ рд╕рд╣реА рд╕рдм-рдПрдЬреЗрдВрдЯ рдХреЛ рдЪреБрдирдирд╛ рд╣реЛрддрд╛ рд╣реИ. рдЗрд╕рдХреЗ рд▓рд┐рдП рдХрд┐рд╕реА рдЯреВрд▓ рдХреА рдЬрд╝рд░реВрд░рдд рдирд╣реАрдВ рд╣реЛрддреА. SubAgents, рдбреЗрд▓рд┐рдЧреЗрд╢рди рдХреЛ рдЕрдкрдиреЗ-рдЖрдк рдореИрдиреЗрдЬ рдХрд░рддрд╛ рд╣реИ.

7. ЁЯУ▒ Flutter рдлрд╝реНрд░рдВрдЯрдПрдВрдб рдЖрд░реНрдХрд┐рдЯреЗрдХреНрдЪрд░

Flutter рдлрд╝реНрд░рдВрдЯрдПрдВрдб, рдЦреБрджрд░рд╛ рдЦрд░реАрджрд╛рд░реА рдХрд░рдиреЗ рд╡рд╛рд▓рд╛ рдкреВрд░реА рддрд░рд╣ рд╕реЗ рдХрд╛рдо рдХрд░рдиреЗ рд╡рд╛рд▓рд╛ рдРрдкреНрд▓рд┐рдХреЗрд╢рди рд╣реИ. рдПрдЖрдИ рдХреА рд╕реБрд╡рд┐рдзрд╛рдПрдВ flutter_frontend/lib/workshop_tasks/ рдореЗрдВ рд▓рд╛рдЗрд╡ рд╣реИрдВ. рдпреЗ core_app/ рдореЗрдВ рдкрд╣рд▓реЗ рд╕реЗ рдореМрдЬреВрдж рд╢реЙрдкрд┐рдВрдЧ рдЕрдиреБрднрд╡ рд╕реЗ рдЕрд▓рдЧ рд╣реИрдВ.

MVVM рдкреИрдЯрд░реНрди

рдпрд╣ рдРрдкреНрд▓рд┐рдХреЗрд╢рди, Provider рдкреИрдХреЗрдЬ рдХреЗ рд╕рд╛рде Model-View-ViewModel рдЖрд░реНрдХрд┐рдЯреЗрдХреНрдЪрд░ рдХрд╛ рдЗрд╕реНрддреЗрдорд╛рд▓ рдХрд░рддрд╛ рд╣реИ:

тФМтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФР    тФМтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФР    тФМтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФР
тФВ  View (Widget)   тФВтЧАтФАтФАтФАтФВ ViewModel (Provider)тФВтЧАтФАтФАтФАтФВ  Service (HTTP)  тФВ
тФВ                  тФВ    тФВ                     тФВ    тФВ                  тФВ
тФВ тАв Renders UI     тФВ    тФВ тАв Holds state       тФВ    тФВ тАв Makes API callsтФВ
тФВ тАв User gestures  тФВтФАтФАтФАтЦ╢тФВ тАв Business logic    тФВтФАтФАтФАтЦ╢тФВ тАв Parses responseтФВ
тФВ тАв Listens for    тФВ    тФВ тАв notifyListeners() тФВ    тФВ тАв Returns data   тФВ
тФВ   state changes  тФВ    тФВ                     тФВ    тФВ                  тФВ
тФФтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФШ    тФФтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФШ    тФФтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФАтФШ

рд╣рд░ рд▓реЗрдпрд░ рдХреА рдПрдХ рддрдп рднреВрдорд┐рдХрд╛ рд╣реЛрддреА рд╣реИ:

  • рдореЙрдбрд▓: Product, Outfit, StyleRequest рдЬреИрд╕реА рдбреЗрдЯрд╛ рдХреНрд▓рд╛рд╕ рдФрд░ TryOnState рдЬреИрд╕реЗ рдПрдирдо
  • ViewModel (ChangeNotifier): рдпрд╣ рдореМрдЬреВрджрд╛ рд╕реНрдерд┐рддрд┐ рдХреЛ рдмрдирд╛рдП рд░рдЦрддрд╛ рд╣реИ рдФрд░ notifyListeners() рдХреЗ рдЬрд╝рд░рд┐рдП рдпреВрдЬрд╝рд░ рдЗрдВрдЯрд░рдлрд╝реЗрд╕ (рдпреВрдЖрдИ) рдореЗрдВ рд╣реБрдП рдмрджрд▓рд╛рд╡реЛрдВ рдХреЛ рдмреНрд░реЙрдбрдХрд╛рд╕реНрдЯ рдХрд░рддрд╛ рд╣реИ
  • рд╡реНрдпреВ (рд╡рд┐рдЬреЗрдЯ): context.watch() рдХреА рдорджрдж рд╕реЗ ViewModel рдХреЛ рд╕рдмреНрд╕рдХреНрд░рд╛рдЗрдм рдХрд░рддрд╛ рд╣реИ рдФрд░ рд╕реНрдЯреЗрдЯ рдмрджрд▓рдиреЗ рдкрд░ рдлрд┐рд░ рд╕реЗ рдмрдирд╛рддрд╛ рд╣реИ
  • Service: рдпрд╣ ADK рдХреЗ рдмреИрдХрдПрдВрдб рдХреЛ рдПрдЪрдЯреАрдЯреАрдкреА рдХреЙрд▓ рдХрд░рддрд╛ рд╣реИ рдФрд░ рдЯрд╛рдЗрдк рдХрд┐рдпрд╛ рдЧрдпрд╛ рдбреЗрдЯрд╛ рджрд┐рдЦрд╛рддрд╛ рд╣реИ

рд╕реЗрд╡рд╛ рд▓реЗрдпрд░

рд╕реЗрд╡рд╛рдУрдВ рдХреЛ рдРрдмреНрд╕рдЯреНрд░реИрдХреНрдЯ рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рдХреЗ рддреМрд░ рдкрд░ рддрдп рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ. рд╕рд╛рде рд╣реА, рдЗрдиреНрд╣реЗрдВ ADK рдХреЗ рд╣рд┐рд╕рд╛рдм рд╕реЗ рд▓рд╛рдЧреВ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ:

// Abstract interface тАФ defines WHAT the service does
abstract class TryItOnService {
 Future<(Uint8List?, String?)> generateTryOnImage(
   Uint8List userImageBytes,
   Uint8List productImageBytes,
 );
}


// Concrete implementation тАФ defines HOW (via ADK REST API)
class AdkFittingRoomService implements TryItOnService { ... }

рдЗрд╕ рддрд░рд╣ рд╕реЗ рдЕрд▓рдЧ рдХрд░рдиреЗ рдХрд╛ рдорддрд▓рдм рд╣реИ рдХрд┐ рдРрдкреНрд▓рд┐рдХреЗрд╢рди рдХреЗ рдмрд╛рдХреА рд╣рд┐рд╕реНрд╕реЗ рдореЗрдВ рдмрджрд▓рд╛рд╡ рдХрд┐рдП рдмрд┐рдирд╛, ADK рдХреЗ рдмреИрдХрдПрдВрдб рдХреЛ Firebase AI, рдореЙрдХ рд╕рд░реНрд╡рд┐рд╕ рдпрд╛ рдХрд┐рд╕реА рдЕрдиреНрдп рд╕реБрд╡рд┐рдзрд╛ рд╕реЗ рдмрджрд▓рд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ.

рдПрдкреАрдЖрдИ рдХреЗ рд▓рд┐рдП рддреАрди рдЪрд░рдгреЛрдВ рд╡рд╛рд▓рд╛ рдкреИрдЯрд░реНрди

AdkFittingRoomService рдФрд░ AdkStylingService, рджреЛрдиреЛрдВ рд╣реА рдПрдбреАрдХреЗ рдмреИрдХрдПрдВрдб рд╕реЗ рдХрдореНрдпреВрдирд┐рдХреЗрдЯ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рд╣реА рдкреИрдЯрд░реНрди рдХрд╛ рдЗрд╕реНрддреЗрдорд╛рд▓ рдХрд░рддреЗ рд╣реИрдВ:

рдкрд╣рд▓рд╛ рдЪрд░рдг: рд╕реЗрд╢рди рдмрдирд╛рдирд╛

Future<String> _createSession() async {
 final url = Uri.parse(
   '$_baseUrl/apps/${Uri.encodeComponent(_appName)}/users/$_userId/sessions');
 final response = await _client.post(url,
   headers: {'Content-Type': 'application/json'},
   body: jsonEncode({}));
 final body = jsonDecode(response.body) as Map<String, dynamic>;
 return body['id'] as String;  // Returns the session ID
}

рджреВрд╕рд░рд╛ рдЪрд░рдг: рдПрдЬреЗрдВрдЯ рдХреЛ рдЪрд▓рд╛рдирд╛

Future<(String?, String?)> _runAgent({required String sessionId, ...}) async {
 final requestBody = jsonEncode({
   'appName': _appName,
   'userId': _userId,
   'sessionId': sessionId,
   'newMessage': {
     'role': 'user',
     'parts': [
       {'text': 'Generate a virtual try-on...'},
       {'inlineData': {'mimeType': 'image/jpeg', 'data': base64Encode(userImageBytes)}},
       {'inlineData': {'mimeType': 'image/png', 'data': base64Encode(productImageBytes)}},
     ],
   },
 });
 final response = await _client.post(Uri.parse('$_baseUrl/run'), body: requestBody);
 // Parse response events for artifact name and GCS URL...
}

рддреАрд╕рд░рд╛ рдЪрд░рдг: рдЖрд░реНрдЯрдлрд╝реИрдХреНрдЯ рдлрд╝реЗрдЪ рдХрд░рдирд╛

Future<Uint8List?> _loadArtifact({required String sessionId, required String artifactName}) async {
 final url = Uri.parse(
   '$_baseUrl/apps/$_appName/users/$_userId/sessions/$sessionId/artifacts/$artifactName');
 final response = await _client.get(url);
 final part = jsonDecode(response.body) as Map<String, dynamic>;
 final data = part['inlineData']['data'] as String;
 return base64Decode(data);  // Returns raw image bytes
}

рдбрд┐рдЬрд╛рдЗрди рдореЗрдВ рдПрдХ рдЕрд╣рдо рдЕрдВрддрд░ рдпрд╣ рд╣реИ рдХрд┐ рдХрдкрдбрд╝реЗ рдЖрдЬрд╝рдорд╛рдиреЗ рдХреА рд╕реЗрд╡рд╛, рд╣рд░ рдЕрдиреБрд░реЛрдз рдХреЗ рд▓рд┐рдП рдПрдХ рдирдпрд╛ рд╕реЗрд╢рди рдмрдирд╛рддреА рд╣реИ (_createSession() рдХреЛ рд╣рд░ рдмрд╛рд░ рдХреЙрд▓ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ). рд╡рд╣реАрдВ, рд╕реНрдЯрд╛рдЗрд▓рд┐рдВрдЧ рд╕реЗрд╡рд╛, рдПрдХ рд╣реА рд╕реЗрд╢рди рдХрд╛ рдлрд┐рд░ рд╕реЗ рдЗрд╕реНрддреЗрдорд╛рд▓ рдХрд░рддреА рд╣реИ (_sessionId ??= await _createSession()), рддрд╛рдХрд┐ рдХрдИ рдмрд╛рд░ рдмрд╛рддрдЪреАрдд рдХреА рдЬрд╛ рд╕рдХреЗ.

рд╕реНрдЯреЗрдЯ рдореИрдиреЗрдЬрдореЗрдВрдЯ: TryItOnProvider

рдлрд╝рд╛рдЗрд▓:

workshop_tasks/step_1_try_it_on/providers/try_it_on_provider.dart

TryItOnProvider, рдЖрдЬрд╝рдорд╛рдиреЗ рдХреА рдкреВрд░реА рдкреНрд░реЛрд╕реЗрд╕ рдХреЛ рдореИрдиреЗрдЬ рдХрд░рддрд╛ рд╣реИ. рдпрд╣ рд╕реНрдЯреЗрдЯ рдорд╢реАрди рдХреЗ рддреМрд░ рдкрд░ TryOnState enum рдХрд╛ рдЗрд╕реНрддреЗрдорд╛рд▓ рдХрд░рддрд╛ рд╣реИ:

enum TryOnState { initial, imagePicked, generating, success, error }


class TryItOnProvider with ChangeNotifier {
 TryOnState _state = TryOnState.initial;
 Uint8List? _userImageBytes;
 Uint8List? _generatedImage;
 String? _errorMessage;

рдирд┐рдЬреА рд╕реНрдерд┐рддрд┐ рдореЗрдВ рдмрджрд▓рд╛рд╡ рдХрд░рдиреЗ рд╕реЗ, рдбреЗрдЯрд╛ рдореЗрдВ рдПрдХрд░реВрдкрддрд╛ рдмрдиреА рд░рд╣рддреА рд╣реИ. рдЗрд╕рдХрд╛ рдорддрд▓рдм рд╣реИ рдХрд┐ рдЖрдкрдХреЛ рдХрднреА рднреА рдкреБрд░рд╛рдиреЗ рдбреЗрдЯрд╛ рдХреЛ рдорд┐рдЯрд╛рдП рдмрд┐рдирд╛ рдФрд░ рдпреВрдЬрд╝рд░ рдЗрдВрдЯрд░рдлрд╝реЗрд╕ (рдпреВрдЖрдИ) рдХреЛ рд╕реВрдЪрдирд╛ рджрд┐рдП рдмрд┐рдирд╛, рд╕реНрдерд┐рддрд┐ рдХреЛ рдЕрдкрдбреЗрдЯ рдирд╣реАрдВ рдХрд░рдирд╛ рдЪрд╛рд╣рд┐рдП:

 void _setGenerating() {
   _state = TryOnState.generating;
   _errorMessage = null;            // Clear any previous error
   _wasLastGenerationCached = false;
   notifyListeners();               // Tell the UI to rebuild
 }


 void _setSuccess(Uint8List image, {bool isCached = false}) {
   _generatedImage = image;
   _errorMessage = null;
   _wasLastGenerationCached = isCached;
   _state = TryOnState.success;
   notifyListeners();
 }

рдЬрдирд░реЗрдЯ рдХрд░рдиреЗ рдХрд╛ рдореБрдЦреНрдп рддрд░реАрдХрд╛, рдЗрди рд╕рднреА рдХреЛ рдПрдХ рд╕рд╛рде рдЬреЛрдбрд╝рддрд╛ рд╣реИ:

 Future<String?> generateTryOnImage(String productImagePath) async {
   final userImageBytes = _userImageBytes;
   if (userImageBytes == null) {
     _setError('No image selected.');
     return _errorMessage;
   }


   // Check local cache first
   final cachedImage = ImageUtils.getCachedImage(_sessionCache, userImageBytes, productUint8List);
   if (cachedImage != null) {
     _setSuccess(cachedImage, isCached: true);
     return null;
   }


   _setGenerating();  // Triggers loading state in UI


   try {
     final (generatedBytes, gcsUrl) = await _aiService.generateTryOnImage(
       userImageBytes, productUint8List);
     if (generatedBytes != null) {
       _fittingGcsUrl = gcsUrl;     // Save for the stylist agent later
       _setSuccess(generatedBytes);
     }
   } catch (e) {
     _setError(e.toString());
   }
   return _state == TryOnState.success ? null : _errorMessage;
 }

рдпреВрдЬрд╝рд░ рдЗрдВрдЯрд░рдлрд╝реЗрд╕ (рдпреВрдЖрдИ): рд╕реНрдЯреЗрдЯ рд░рд╛рдКрдЯрд░ рдХреЗ рддреМрд░ рдкрд░ рд╕реНрдХреНрд░реАрди

рдлрд╝рд╛рдЗрд▓:

workshop_tasks/step_1_try_it_on/ui/2_try_it_on_screen.dart

рдХрдкрдбрд╝реЗ рдЖрдЬрд╝рдорд╛рдиреЗ рдХреА рд╕реБрд╡рд┐рдзрд╛ рд╡рд╛рд▓реА рд╕реНрдХреНрд░реАрди, Dart 3 рдХреА рдкреИрдЯрд░реНрди рдореИрдЪрд┐рдВрдЧ рд╕реБрд╡рд┐рдзрд╛ рдХрд╛ рдЗрд╕реНрддреЗрдорд╛рд▓ рдХрд░рддреА рд╣реИ. рдЗрд╕рд╕реЗ AnimatedSwitcher рдХреЗ рд╕рд╛рде рдорд┐рд▓рдХрд░, рд╕рдм-рд╕реНрдХреНрд░реАрди рдХреЗ рдмреАрдЪ рд░реВрдЯ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ. рдРрд╕рд╛, рд╕реЗрд╡рд╛ рджреЗрдиреЗ рд╡рд╛рд▓реА рдХрдВрдкрдиреА рдХреА рд╕реНрдерд┐рддрд┐ рдХреЗ рдЖрдзрд╛рд░ рдкрд░ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ:

class TryItOnScreen extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
   final tryOnProvider = context.watch<TryItOnProvider>();


   return ScaffoldWithBackgroundNoise(
     appBar: const TryOnAppBar(),
     body: AnimatedSwitcher(
       duration: AppDurations.medium,
       child: switch (tryOnProvider.state) {
         TryOnState.initial || TryOnState.error => const ChooseImageScreen(),
         TryOnState.imagePicked || TryOnState.generating => LoadingScreen(
           userImage: tryOnProvider.userImageBytes),
         TryOnState.success => const FittingRoomScreen(),
       },
     ),
   );
 }
}

context.watch() рдиреЗ рд╕реЗрд╡рд╛ рджреЗрдиреЗ рд╡рд╛рд▓реА рдХрдВрдкрдиреА рдХреА рд╕рджрд╕реНрдпрддрд╛ рд▓реА рд╣реЛ. рдЬрдм рднреА notifyListeners() рдХреЛ рдХреЙрд▓ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдпрд╣ рд╡рд┐рдЬреЗрдЯ рдлрд┐рд░ рд╕реЗ рдмрдирддрд╛ рд╣реИ. рд╕рд╛рде рд╣реА, AnimatedSwitcher рдПрдХ рд╕реНрдХреНрд░реАрди рд╕реЗ рджреВрд╕рд░реА рд╕реНрдХреНрд░реАрди рдкрд░ рдЖрд╕рд╛рдиреА рд╕реЗ рдЯреНрд░рд╛рдВрдЬрд╝рд┐рд╢рди рдХрд░рддрд╛ рд╣реИ. рдХреЛрдИ Navigator.push рдирд╣реАрдВ рд╣реИ тАФ рд╕реНрдХреНрд░реАрди рдХрд╛ рдХреЙрдиреНрдЯреЗрдВрдЯ, рд╕реНрдЯреЗрдЯ рдПрдирдо рдХреЗ рдЖрдзрд╛рд░ рдкрд░ рдмрджрд▓рддрд╛ рд╣реИ.

рдПрдЬреЗрдВрдЯрд┐рдХ рд╣реИрдВрдбрдСрдлрд╝: рдлрд╝рд┐рдЯрд┐рдВрдЧ рд░реВрдо тЖТ рд╕реНрдЯрд╛рдЗрд▓рд┐рд╕реНрдЯ

рд╕рдмрд╕реЗ рджрд┐рд▓рдЪрд╕реНрдк UX рдкреИрдЯрд░реНрди рдпрд╣ рд╣реИ рдХрд┐ рдРрдкреНрд▓рд┐рдХреЗрд╢рди, рдлрд╝рд┐рдЯрд┐рдВрдЧ рд░реВрдо рдПрдЬреЗрдВрдЯ рд╕реЗ рд╕реНрдЯрд╛рдЗрд▓рд┐рд╕реНрдЯ рдПрдЬреЗрдВрдЯ рдХреЛ рдХреЙрдиреНрдЯреЗрдХреНрд╕реНрдЯ рдХреИрд╕реЗ рдкрд╛рд╕ рдХрд░рддрд╛ рд╣реИ.

5_fitting_room.dart рдореЗрдВ, рд╡рд░реНрдЪреБрдЕрд▓ рддреМрд░ рдкрд░ рдЖрдЬрд╝рдорд╛рдиреЗ рдХреА рдЗрдореЗрдЬ рдЬрдирд░реЗрдЯ рд╣реЛрдиреЗ рдХреЗ рдмрд╛рдж, "рдореБрдЭреЗ рд╕реНрдЯрд╛рдЗрд▓ рдХрд░реЛ" рдмрдЯрди рдкрд░ рдХреНрд▓рд┐рдХ рдХрд░рдиреЗ рд╕реЗ рдПрдХ рдлрд╝реЙрд░реНрдо рдЦреБрд▓рддрд╛ рд╣реИ. рдЬрдм рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдпрд╣ рд╕рдмрдорд┐рдЯ рдХрд░рддрд╛ рд╣реИ:

// From 1_style_me_form_sheet.dart
Navigator.pop(context, StyleRequest(
 location: _locationController.text.trim(),
 occasion: _occasionController.text.trim(),
 notes: _notesController.text.trim(),
 gcsUserImageUrl: provider.fittingGcsUrl,          // GCS URI from fitting result
 userImageData: provider.fittingGcsUrl == null
     ? provider.userImageBytes : null,              // Fallback to raw bytes
 selectedProductId: provider.selectedProduct?.id,   // Product they already tried on
 selectedProductTitle: provider.selectedProduct?.title,
));

StyleRequest рдореЗрдВ рд╕реНрдЯрд╛рдЗрд▓рд┐рд╕реНрдЯ рдХреА рдЬрд╝рд░реВрд░рдд рдХреА рд╣рд░ рдЪреАрдЬрд╝ рд╢рд╛рдорд┐рд▓ рд╣реЛрддреА рд╣реИ:

  • рдЬрдЧрд╣ рдФрд░ рдЕрд╡рд╕рд░ тАФ рд╕реНрдЯрд╛рдЗрд▓рд┐рдВрдЧ рдХреЗ рд▓рд┐рдП рдЯреЗрдХреНрд╕реНрдЯ рдХреЙрдиреНрдЯреЗрдХреНрд╕реНрдЯ
  • GCS рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреА рдЗрдореЗрдЬ рдХрд╛ рдпреВрдЖрд░рдПрд▓ тАФ рддрд╛рдХрд┐ рд╕реНрдЯрд╛рдЗрд▓рд┐рд╕реНрдЯ, рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреА рдЙрд╕реА рдЗрдореЗрдЬ рдХрд╛ рдлрд┐рд░ рд╕реЗ рдЗрд╕реНрддреЗрдорд╛рд▓ рдХрд░ рд╕рдХреЗ
  • рдЪреБрдирд╛ рдЧрдпрд╛ рдкреНрд░реЙрдбрдХреНрдЯ тАФ рддрд╛рдХрд┐ рд╕реНрдЯрд╛рдЗрд▓рд┐рд╕реНрдЯ рдЗрд╕реЗ рд╣рд░ рдкреЛрд╢рд╛рдХ рдореЗрдВ рд╢рд╛рдорд┐рд▓ рдХрд░ рд╕рдХреЗ

рдЗрд╕реЗ рдПрдЬреЗрдВрдЯрд┐рдХ рд╣реИрдВрдбрдСрдлрд╝ рдХрд╣рд╛ рдЬрд╛рддрд╛ рд╣реИ. рдЗрд╕рдореЗрдВ рдорд▓реНрдЯреАрдореЙрдбрд▓ рдХреЙрдиреНрдЯреЗрдХреНрд╕реНрдЯ рдХреЛ рдПрдХ рдПрдЖрдИ рдПрдЬреЗрдВрдЯ рд╕реЗ рджреВрд╕рд░реЗ рдПрдЖрдИ рдПрдЬреЗрдВрдЯ рдХреЛ рдЖрд╕рд╛рдиреА рд╕реЗ рдЯреНрд░рд╛рдВрд╕рдлрд╝рд░ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ. рд╕рд╛рде рд╣реА, рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреЛ рд╕рд┐рд░реНрдлрд╝ рдПрдХ рд╕рд╛рдорд╛рдиреНрдп рдлрд╝реЙрд░реНрдо рджрд┐рдЦрддрд╛ рд╣реИ.

рд╕реНрдЯрд╛рдЗрд▓рд┐рдВрдЧ рдлрд╝реНрд▓реЛ: StylingProvider

рдлрд╝рд╛рдЗрд▓:

workshop_tasks/step_2_style_me/providers/styling_provider.dart

TryItOnProvider рдХреА рддреБрд▓рдирд╛ рдореЗрдВ StylingProvider рдЬрд╝реНрдпрд╛рджрд╛ рдЖрд╕рд╛рди рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рдпрд╣ рдЬрд╝реНрдпрд╛рджрд╛рддрд░ рдЬрдЯрд┐рд▓рддрд╛ рдХреЛ рдмреИрдХрдПрдВрдб рдХреЛ рд╕реМрдВрдк рджреЗрддрд╛ рд╣реИ:

class StylingProvider with ChangeNotifier {
 StylingState _state = StylingState.initial;
 List<Outfit> _outfits = [];


 Future<bool> getStyleSuggestions(StyleRequest request) async {
   if (_state == StylingState.loading) return false;  // Prevent spam
   _currentRequest = request;
   _setLoading();
   try {
     final suggestions = await _stylingService.getStyleSuggestions(request);
     _setSuccess(suggestions);
     return true;
   } catch (e) {
     _setError('Something went wrong.');
   }
   return false;
 }


 // Multi-turn refinement тАФ uses the same session
 Future<bool> refineWithFeedback(String feedback) async {
   if (_state == StylingState.loading) return false;
   _setLoading();
   try {
     final suggestions = await _stylingService.refineWithFeedback(feedback);
     _setSuccess(suggestions);
     return true;
   } catch (e) {
     _setError('Something went wrong.');
   }
   return false;
 }
}

refineWithFeedback рддрд░реАрдХреЗ рд╕реЗ, рдЙрд╕реА рд╕реЗрд╢рди рдореЗрдВ рд╕рд╛рджрд╛ рдЯреЗрдХреНрд╕реНрдЯ рдореИрд╕реЗрдЬ рднреЗрдЬрд╛ рдЬрд╛рддрд╛ рд╣реИ. рдмреИрдХрдПрдВрдб рдХреЗ InjectPreviousProducts рдФрд░ ExtractAndInjectUserImage рдХреЙрд▓рдмреИрдХ, рдХреЙрдиреНрдЯреЗрдХреНрд╕реНрдЯ рдореИрдиреЗрдЬрдореЗрдВрдЯ рдХреЛ рдЕрдкрдиреЗ-рдЖрдк рд╣реИрдВрдбрд▓ рдХрд░рддреЗ рд╣реИрдВ.

8. ЁЯЪА рдРрдкреНрд▓рд┐рдХреЗрд╢рди рдХреЛ рд╕реНрдерд╛рдиреАрдп рддреМрд░ рдкрд░ рдЪрд▓рд╛рдирд╛

Cloud Shell рдХрд╛ рдмреЗрд╣рддрд░ рдЕрдиреБрднрд╡ рдкрд╛рдиреЗ рдХреЗ рд▓рд┐рдП, Go рдмреИрдХрдПрдВрдб, рдХрдВрдкрд╛рдЗрд▓ рдХрд┐рдП рдЧрдП Flutter рд╡реЗрдм рдРрдкреНрд▓рд┐рдХреЗрд╢рди рдХреЛ рдЙрд╕реА рдкреЛрд░реНрдЯ (8080) рд╕реЗ рджрд┐рдЦрд╛рддрд╛ рд╣реИ. рдПрдХ рдкреНрд░реЛрд╕реЗрд╕, рдПрдХ рдЭрд▓рдХ рдпреВрдЖрд░рдПрд▓, рдХреНрд░реЙрд╕-рдСрд░рд┐рдЬрд┐рди рд╕реЗ рдЬреБрдбрд╝реА рдХреЛрдИ рд╕рдорд╕реНрдпрд╛ рдирд╣реАрдВ, рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди рдлрд╝рд╛рдЗрд▓реЛрдВ рдореЗрдВ рдмрджрд▓рд╛рд╡ рдХрд░рдиреЗ рдХреА рдЬрд╝рд░реВрд░рдд рдирд╣реАрдВ.

рд╢реБрд░реВ рдХрд░рдиреЗ рд╕реЗ рдкрд╣рд▓реЗ тАФ рдПрдбреАрд╕реА рдХреА рдЬрд╛рдВрдЪ рдХрд░рдирд╛

Vertex AI рдХреЛ рдХреЙрд▓ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдмреИрдХрдПрдВрдб рдХреЛ рдРрдкреНрд▓рд┐рдХреЗрд╢рди рдбрд┐рдлрд╝реЙрд▓реНрдЯ рдХреНрд░реЗрдбреЗрдВрд╢рд┐рдпрд▓ рдХреА рдЬрд╝рд░реВрд░рдд рд╣реЛрддреА рд╣реИ. рдЕрдЧрд░ рдЖрдкрдиреЗ рдЗрд╕ Cloud Shell рд╕реЗрд╢рди рдФрд░ рдЗрд╕ Google рдЦрд╛рддреЗ рдореЗрдВ рдкреНрд░реЛрдЬреЗрдХреНрдЯ рд╕реЗрдЯрдЕрдк рдХрд░рдиреЗ рдХрд╛ рд╕рд╛рддрд╡рд╛рдВ рдЪрд░рдг рдкреВрд░рд╛ рдХрд░ рд▓рд┐рдпрд╛ рд╣реИ, рддреЛ рдЖрдкрдХреЛ рдХреБрдЫ рдФрд░ рдХрд░рдиреЗ рдХреА рдЬрд╝рд░реВрд░рдд рдирд╣реАрдВ рд╣реИ. рдЕрдЧрд░ рдЖрдкрдиреЗ рдХреБрдЫ рд╕рдордп рдмрд╛рдж рд╡рд╛рдкрд╕ рдЖрдХрд░ рдЦрд╛рддрд╛ рдЗрд╕реНрддреЗрдорд╛рд▓ рдХрд░рдирд╛ рд╢реБрд░реВ рдХрд┐рдпрд╛ рд╣реИ, рдЦрд╛рддрд╛ рдмрджрд▓рд╛ рд╣реИ рдпрд╛ рдЖрдкрдХреЛ рдкрдХреНрдХрд╛ рдирд╣реАрдВ рд╣реИ рдХрд┐ рдЖрдкрдиреЗ рдХреМрдирд╕реЗ рдЦрд╛рддреЗ рд╕реЗ рд╕рд╛рдЗрди рдЗрди рдХрд┐рдпрд╛ рд╣реИ, рддреЛ рдкрд╛рдВрдЪ рд╕реЗрдХрдВрдб рдореЗрдВ рдЗрд╕рдХреА рдкреБрд╖реНрдЯрд┐ рдХрд░реЗрдВ:

gcloud auth application-default print-access-token | head -c 20 && echo "..."

рдЕрдЧрд░ рдЗрд╕рд╕реЗ рдЯреЛрдХрди рдХреЗ ~20 рд╡рд░реНрдг рдкреНрд░рд┐рдВрдЯ рд╣реЛрддреЗ рд╣реИрдВ, рддреЛ рдЗрд╕рдХрд╛ рдорддрд▓рдм рд╣реИ рдХрд┐ рд╕рдм рдХреБрдЫ рдареАрдХ рд╣реИ. рдЕрдЧрд░ рдХреЛрдИ рдЧрдбрд╝рдмрдбрд╝реА рд╣реЛрддреА рд╣реИ, рддреЛ рдкреНрд░реЛрдЬреЗрдХреНрдЯ рд╕реЗрдЯрдЕрдк рдХрд░рдиреЗ рдХрд╛ рд╕рд╛рддрд╡рд╛рдВ рдЪрд░рдг рдлрд┐рд░ рд╕реЗ рдкреВрд░рд╛ рдХрд░реЗрдВ:

gcloud auth application-default login
gcloud auth application-default set-quota-project $(gcloud config get-value project)

рдЖрдкрдХреЛ рджреЛ Cloud Shell рдЯрд░реНрдорд┐рдирд▓ рдЗрд╕реНрддреЗрдорд╛рд▓ рдХрд░рдиреЗ рд╣реЛрдВрдЧреЗ:

  • рдЯрд░реНрдорд┐рдирд▓ A тАФ рдпрд╣ рдмреИрдХрдПрдВрдб рдХреЛ рд▓рдЧрд╛рддрд╛рд░ рдЪрд▓рд╛рддрд╛ рд╣реИ (./run.sh). рдЗрд╕реЗ рдЦреБрд▓рд╛ рд░рдЦреЗрдВ.
  • рдЯрд░реНрдорд┐рдирд▓ B тАФ Flutter рдХреЗ рд╡реЗрдм рдмрд┐рд▓реНрдб рдХреЛ рдПрдХ рдмрд╛рд░ рдЪрд▓рд╛рддрд╛ рд╣реИ (flutter build web). рдХрд╛рдо рдкреВрд░рд╛ рд╣реЛрдиреЗ рдкрд░ рдмрдВрдж рд╣реЛ рдЬрд╛рддрд╛ рд╣реИ.

рдЗрдирдореЗрдВ рд╕реЗ рдХреЛрдИ рднреА рдХрд╛рдо рдкрд╣рд▓реЗ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ. рд╣рд╛рд▓рд╛рдВрдХрд┐, рдкрд╣рд▓реА рдмрд╛рд░ рдореЗрдВ рдмреЗрд╣рддрд░ рдЕрдиреБрднрд╡ рдкрд╛рдиреЗ рдХреЗ рд▓рд┐рдП, рдкрд╣рд▓реЗ Flutter рдмрдирд╛рдПрдВ. рдЗрд╕рд╕реЗ рдмреИрдХрдПрдВрдб рдХреЗ рдкрд╛рд╕, рд╢реБрд░реВ рд╣реЛрдиреЗ рдХреЗ рд╕рд╛рде рд╣реА рдпреВрдЬрд╝рд░ рдЗрдВрдЯрд░рдлрд╝реЗрд╕ (рдпреВрдЖрдИ) рдЙрдкрд▓рдмреНрдз рд╣реЛрдЧрд╛.

1. рдЯрд░реНрдорд┐рдирд▓ B тАФ Build the Flutter Web Bundle (one-shot)

рдирдпрд╛ Cloud Shell рдЯреИрдм рдЦреЛрд▓реЗрдВ (рдЯрд░реНрдорд┐рдирд▓ рдкреИрдирд▓ рдХреЗ рд╕рдмрд╕реЗ рдКрдкрд░ рдореМрдЬреВрдж +), рдлрд┐рд░:

cd ~/fashion_app_demo/flutter_frontend
flutter pub get
flutter build web

рдЗрд╕рд╕реЗ flutter_frontend/build/web/ рддреИрдпрд╛рд░ рд╣реЛрддрд╛ рд╣реИ. рдпрд╣ рд╕реНрдЯреИрдЯрд┐рдХ рдлрд╝рд╛рдЗрд▓реЛрдВ (рдПрдЪрдЯреАрдПрдордПрд▓, JS, рдРрд╕реЗрдЯ) рдХреА рдПрдХ рдбрд╛рдпрд░реЗрдХреНрдЯреНрд░реА рд╣реЛрддреА рд╣реИ. рдпрд╣ рдкреНрд░реЛрд╕реЗрд╕ рдкреВрд░реА рд╣реЛрдиреЗ рдХреЗ рдмрд╛рдж рдмрдВрдж рд╣реЛ рдЬрд╛рддреА рд╣реИ. рдмреИрдХрдПрдВрдб, рдбрд╛рдпрд░реЗрдХреНрдЯреНрд░реА рдХреЗ рдореМрдЬреВрдж рд╣реЛрдиреЗ рдХрд╛ рдкрддрд╛ рдЪрд▓рддреЗ рд╣реА рдЗрдиреНрд╣реЗрдВ рджрд┐рдЦрд╛ рджреЗрдЧрд╛.

2. рдЯрд░реНрдорд┐рдирд▓ A тАФ рдмреИрдХрдПрдВрдб рд╢реБрд░реВ рдХрд░реЗрдВ (рд▓рдВрдмреЗ рд╕рдордп рддрдХ рдЪрд▓рдиреЗ рд╡рд╛рд▓рд╛)

рдЕрдкрдиреЗ рдУрд░рд┐рдЬрдирд▓ Cloud Shell рдЯрд░реНрдорд┐рдирд▓ рдореЗрдВ:

cd ~/fashion_app_demo/adk_backend
./run.sh

рдЖрдкрдХреЛ рдЗрд╕ рддрд░рд╣ рдХреА рд╡рд┐рдВрдбреЛ рджрд┐рдЦреЗрдЧреА:

Serving Flutter web build from ../flutter_frontend/build/web

рдЗрд╕ рдЯрд░реНрдорд┐рдирд▓ рдХреЛ рдЪрд╛рд▓реВ рд░рдЦреЗрдВ тАФ рдмреИрдХрдПрдВрдб рддрдм рддрдХ рдЪрд╛рд▓реВ рд░рд╣реЗрдЧрд╛, рдЬрдм рддрдХ run.sh рдЪрд╛рд▓реВ рд╣реИ. рдЗрд╕реЗ рд░реЛрдХрдиреЗ рдХреЗ рд▓рд┐рдП, Ctrl+C рдкрд░ рдЯреИрдк рдХрд░реЗрдВ.

рд╕рд░реНрд╡рд░, рдкреЛрд░реНрдЯ 8080 рдкрд░ рдореМрдЬреВрдж рд╕рднреА рдЪреАрдЬрд╝реЛрдВ рдХреЛ рджрд┐рдЦрд╛рддрд╛ рд╣реИ:

  • / тАФ Flutter рд╡реЗрдм рдРрдкреНрд▓рд┐рдХреЗрд╢рди (рдЦрд░реАрджрд╛рд░реА рдХрд╛ рдпреВрдЬрд╝рд░ рдЗрдВрдЯрд░рдлрд╝реЗрд╕)
  • /api/ тАФ ADK REST рдПрдВрдбрдкреЙрдЗрдВрдЯ (рдЗрдиреНрд╣реЗрдВ Flutter рдРрдкреНрд▓рд┐рдХреЗрд╢рди рдХреЙрд▓ рдХрд░рддрд╛ рд╣реИ)
  • ADK Dev UI тАФ рдпрд╣ / рдкрд░ рднреА рдЙрдкрд▓рдмреНрдз рд╣реЛрддрд╛ рд╣реИ, рдЬрдм рдХреЛрдИ Flutter рдмрд┐рд▓реНрдб рдирд╣реАрдВ рд╣реЛрддрд╛ рд╣реИ. рдпрд╣ рдПрдЬреЗрдВрдЯ рдХреЛ рд╕реАрдзреЗ рддреМрд░ рдкрд░ рдбреАрдмрдЧ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдлрд╝рд╛рдпрджреЗрдордВрдж рд╣реЛрддрд╛ рд╣реИ

3. рд╡реЗрдм рдХреА рдЭрд▓рдХ рджреЗрдЦрдиреЗ рдХреА рд╕реБрд╡рд┐рдзрд╛ рдЦреЛрд▓рдирд╛

  1. Cloud Shell рдореЗрдВ, рд╕рдмрд╕реЗ рдКрдкрд░ рджрд╛рдИрдВ рдУрд░ рдореМрдЬреВрдж рд╡реЗрдм рдкреНрд░реАрд╡реНрдпреВ рдЖрдЗрдХреЙрди тЖТ рдкреЛрд░реНрдЯ 8080 рдкрд░ рдкреНрд░реАрд╡реНрдпреВ рдХрд░реЗрдВ рдкрд░ рдХреНрд▓рд┐рдХ рдХрд░реЗрдВ
  2. Flutter рд╢реЙрдкрд┐рдВрдЧ рдРрдкреНрд▓рд┐рдХреЗрд╢рди, рдирдП рдЯреИрдм рдореЗрдВ рд▓реЛрдб рд╣реЛрддрд╛ рд╣реИ
  3. рдкреНрд░реЙрдбрдХреНрдЯ рдХреИрдЯрд▓реЙрдЧ рдмреНрд░рд╛рдЙрдЬрд╝ рдХрд░реЗрдВ рдФрд░ рдХреЛрдИ рдЖрдЗрдЯрдо рдЪреБрдиреЗрдВ
  4. рдХрдкрдбрд╝реЗ рдЖрдЬрд╝рдорд╛рдиреЗ рдХреА рд╕реБрд╡рд┐рдзрд╛ рд╢реБрд░реВ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рд╡реНрдпрдХреНрддрд┐ рдХреЗ рдЖрдЗрдХреЙрди (ЁЯСд) рдкрд░ рдЯреИрдк рдХрд░реЗрдВ
  5. рдХреЛрдИ рдлрд╝реЛрдЯреЛ рдЕрдкрд▓реЛрдб рдХрд░реЗрдВ рдФрд░ рдПрдЖрдИ рдХреЛ, рдЖрдЬрд╝рдорд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдЗрдореЗрдЬ рдЬрдирд░реЗрдЯ рдХрд░рддреЗ рд╣реБрдП рджреЗрдЦреЗрдВ
  6. рдХрдкрдбрд╝реЛрдВ рдХреЗ рд╕реБрдЭрд╛рд╡ рдкрд╛рдиреЗ рдХреЗ рд▓рд┐рдП, "рд╕реНрдЯрд╛рдЗрд▓ рдореА" рдкрд░ рдЯреИрдк рдХрд░реЗрдВ
  7. рдлрд╝реЙрд▓реЛ-рдЕрдк рдлрд╝реАрдбрдмреИрдХ рдЯрд╛рдЗрдк рдХрд░реЗрдВ, рдЬреИрд╕реЗ рдХрд┐ "рдЗрд╕реЗ рдФрд░ рдЬрд╝реНрдпрд╛рджрд╛ рд╕рд╛рдорд╛рдиреНрдп рдмрдирд╛рдУ" тАФ рдПрдХ рд╣реА рд╕реЗрд╢рди рдореЗрдВ рдмреЗрд╣рддрд░ рдмрдирд╛рдирд╛

9. тШБя╕П Cloud Run рдкрд░ рдбрд┐рдкреНрд▓реЙрдп рдХрд░рдирд╛

Flutter Build рдХреЛ рдмреИрдХрдПрдВрдб рдореЗрдВ рдмрдВрдбрд▓ рдХрд░рдирд╛

Cloud Run рдХрдВрдЯреЗрдирд░, рдПрдкреАрдЖрдИ рдФрд░ рдпреВрдЬрд╝рд░ рдЗрдВрдЯрд░рдлрд╝реЗрд╕ (рдпреВрдЖрдИ), рджреЛрдиреЛрдВ рдХреЛ рдПрдХ рдЗрдореЗрдЬ рд╕реЗ рд╢рд┐рдк рдХрд░рддрд╛ рд╣реИ. Flutter рд╡реЗрдм рдмрд┐рд▓реНрдб рдХреЛ adk_backend/flutter_web/ рдореЗрдВ рдХреЙрдкреА рдХрд░реЗрдВ. рдЬрдм Go рд╕рд░реНрд╡рд░ рдХреЛ рдпрд╣ рддрдп рдХрд░рдирд╛ рд╣реЛрддрд╛ рд╣реИ рдХрд┐ рдХреМрдирд╕реЗ рдпреВрдЬрд╝рд░ рдЗрдВрдЯрд░рдлрд╝реЗрд╕ (рдпреВрдЖрдИ) рдХреЛ рджрд┐рдЦрд╛рдирд╛ рд╣реИ, рддрдм рд╡рд╣ рд╕рдмрд╕реЗ рдкрд╣рд▓реЗ рдЗрд╕ рдкрд╛рде рдХреА рдЬрд╛рдВрдЪ рдХрд░рддрд╛ рд╣реИ:

cd ~/fashion_app_demo/flutter_frontend
flutter build web
rm -rf ../adk_backend/flutter_web
cp -r build/web ../adk_backend/flutter_web

(рдЕрдЧрд░ рдЖрдкрдиреЗ рд╕реНрдерд╛рдиреАрдп рддреМрд░ рдкрд░ рдмрджрд▓рд╛рд╡ рдХрд┐рдП рд╣реИрдВ, рддреЛ рд╣реЛ рд╕рдХрддрд╛ рд╣реИ рдХрд┐ рдЖрдкрдХреЗ рдкрд╛рд╕ Run-Locally рдЪрд░рдг рд╕реЗ рдкрд╣рд▓реЗ рд╣реА build/web рдореМрдЬреВрдж рд╣реЛ. flutter build web рдХреЛ рдлрд┐рд░ рд╕реЗ рдЪрд▓рд╛рдиреЗ рдкрд░ рднреА рдХреЛрдИ рд╕рдорд╕реНрдпрд╛ рдирд╣реАрдВ рд╣реЛрдЧреА.)

рдмреИрдХрдПрдВрдб рдХреЛ рдбрд┐рдкреНрд▓реЙрдп рдХрд░рдирд╛ (рдПрдкреАрдЖрдИ + рдпреВрдЬрд╝рд░ рдЗрдВрдЯрд░рдлрд╝реЗрд╕ (рдпреВрдЖрдИ) рдЙрдкрд▓рдмреНрдз рдХрд░рд╛рддрд╛ рд╣реИ)

cd ~/fashion_app_demo/adk_backend

gcloud run deploy fashion-app-backend \
 --source . \
 --region us-central1 \
 --allow-unauthenticated \
 --set-env-vars "GOOGLE_CLOUD_PROJECT=$PROJECT_ID,GCS_BUCKET=fashion-app-$PROJECT_ID" \
 --memory 1Gi \
 --cpu 2 \
 --timeout 300s \
 --min-instances 0 \
 --max-instances 3

рдбрд┐рдкреНрд▓реЙрдп рд╣реЛрдиреЗ рдХреЗ рдмрд╛рдж, рдЖрдкрдХреЛ https://fashion-app-backend-xyz-uc.a.run.app рдЬреИрд╕рд╛ рд╕реЗрд╡рд╛ рдХрд╛ рдпреВрдЖрд░рдПрд▓ рдорд┐рд▓реЗрдЧрд╛. рдЗрд╕реЗ рдмреНрд░рд╛рдЙрдЬрд╝рд░ рдореЗрдВ рдЦреЛрд▓реЗрдВ тАФ Flutter рд╢реЙрдкрд┐рдВрдЧ рдРрдкреНрд▓рд┐рдХреЗрд╢рди, / рд╕реЗ рд▓реЛрдб рд╣реЛрддрд╛ рд╣реИ. рд╕рд╛рде рд╣реА, рдЗрд╕рдХреЗ рдПрдкреАрдЖрдИ рдХреЙрд▓ рдПрдХ рд╣реА рд╣реЛрд╕реНрдЯ рдкрд░ /api/ рдкрд░ рдЬрд╛рддреЗ рд╣реИрдВ. рдлрд╝реНрд░рдВрдЯрдПрдВрдб рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди рдореЗрдВ рдмрджрд▓рд╛рд╡ рдХрд░рдиреЗ рдХреА рдЬрд╝рд░реВрд░рдд рдирд╣реАрдВ рд╣реИ. рд╕рд╛рде рд╣реА, рдХреЛрдИ рдПрдкреАрдЖрдИ рдкрд╛рд╕рдХреЛрдб рдирд╣реАрдВ рднреЗрдЬрд╛ рдЧрдпрд╛ рд╣реИ.

рдбрд┐рдкреНрд▓реЙрдпрдореЗрдВрдЯ рдХреА рдкреБрд╖реНрдЯрд┐ рдХрд░рдирд╛

рдЕрдкрдиреЗ рдмреНрд░рд╛рдЙрдЬрд╝рд░ рдореЗрдВ Cloud Run рдХрд╛ рдпреВрдЖрд░рдПрд▓ рдЦреЛрд▓реЗрдВ рдФрд░ рдкреВрд░рд╛ рдлрд╝реНрд▓реЛ рдЖрдЬрд╝рдорд╛рдПрдВ:

  1. рдмреНрд░рд╛рдЙрдЬрд╝ рдХрд░реЗрдВ тЖТ рдХреЛрдИ рдкреНрд░реЙрдбрдХреНрдЯ рдЪреБрдиреЗрдВ
  2. рдЖрдЬрд╝рдорд╛рдПрдВ тЖТ рдЕрдкрдиреА рдлрд╝реЛрдЯреЛ рдЕрдкрд▓реЛрдб рдХрд░реЗрдВ тЖТ рдПрдЖрдИ рд╕реЗ рдЬрдирд░реЗрдЯ рдХреА рдЧрдИ рдЗрдореЗрдЬ рджреЗрдЦреЗрдВ
  3. рд╕реНрдЯрд╛рдЗрд▓ рдореА тЖТ рдЬрдЧрд╣/рдореМрдХреЗ рдХреА рдЬрд╛рдирдХрд╛рд░реА рднрд░реЗрдВ тЖТ рдХреНрдпреВрд░реЗрдЯ рдХрд┐рдП рдЧрдП рдХрдкрдбрд╝реЗ рджреЗрдЦреЗрдВ
  4. рд╕реБрдЭрд╛рд╡/рд░рд╛рдп рджреЗрдВ рдпрд╛ рд╢рд┐рдХрд╛рдпрдд рдХрд░реЗрдВ тЖТ "рдЗрд╕реЗ рдЬрд╝реНрдпрд╛рджрд╛ рдХреИрдЬрд╝реБрдЕрд▓ рдмрдирд╛рдУ" рдЯрд╛рдЗрдк рдХрд░реЗрдВ тЖТ рдЕрдкрдбреЗрдЯ рдХрд┐рдП рдЧрдП рдХрдкрдбрд╝реЗ рджреЗрдЦреЗрдВ
  5. рдмреИрдЧ рдореЗрдВ рдЬреЛрдбрд╝реЗрдВ тЖТ рдЦрд░реАрджрд╛рд░реА рдХреА рдкреНрд░реЛрд╕реЗрд╕ рдкреВрд░реА рдХрд░реЗрдВ

10. ЁЯОЙ рдирд┐рд╖реНрдХрд░реНрд╖

рдЖрдкрдиреЗ рдХреНрдпрд╛ рдмрдирд╛рдпрд╛

рдЖрдкрдиреЗ рдПрдЖрдИ рдХреА рдорджрдж рд╕реЗ рдХрд╛рдо рдХрд░рдиреЗ рд╡рд╛рд▓реА рдЦреБрджрд░рд╛ рд╢реЙрдкрд┐рдВрдЧ рдХреА рд╕рднреА рд╕реБрд╡рд┐рдзрд╛рдУрдВ рдХрд╛ рдЗрд╕реНрддреЗрдорд╛рд▓ рдХрд┐рдпрд╛ рд╣реИ. рдЬреИрд╕реЗ:

  • тЬЕ рдорд▓реНрдЯреА-рдПрдЬреЗрдВрдЯ рдмреИрдХрдПрдВрдб, рдЬрд┐рд╕рдореЗрдВ рдЪрд╛рд░ рд╡рд┐рд╢реЗрд╖рдЬреНрдЮ рдПрдЬреЗрдВрдЯ рдПрдХ рд╕рд╛рде рдХрд╛рдо рдХрд░рддреЗ рд╣реИрдВ
  • тЬЕ рд╡рд░реНрдЪреБрдЕрд▓ рдлрд╝рд┐рдЯрд┐рдВрдЧ рд░реВрдо, рдЬреЛ рд╣рд░ рд╡реНрдпрдХреНрддрд┐ рдХреЗ рд╣рд┐рд╕рд╛рдм рд╕реЗ, рдХрдкрдбрд╝реЗ рдЖрдЬрд╝рдорд╛рдиреЗ рдХреА рдЗрдореЗрдЬ рдЬрдирд░реЗрдЯ рдХрд░рддрд╛ рд╣реИ
  • тЬЕ рдПрдХ рдПрдЖрдИ рд╕реНрдЯрд╛рдЗрд▓рд┐рд╕реНрдЯ, рдЬреЛ рдмрд╛рддрдЪреАрдд рдХреЗ рдЬрд╝рд░рд┐рдП рдХрдкрдбрд╝реЛрдВ рдХреЗ рдХреЙрдореНрдмрд┐рдиреЗрд╢рди рдмрдирд╛рддрд╛ рд╣реИ рдФрд░ рдЙрдиреНрд╣реЗрдВ рдмреЗрд╣рддрд░ рдмрдирд╛рддрд╛ рд╣реИ
  • тЬЕ рдХреНрд░реЙрд╕-рдкреНрд▓реИрдЯрдлрд╝реЙрд░реНрдо Flutter рдРрдкреНрд▓рд┐рдХреЗрд╢рди, рдЬреЛ рдПрдЬреЗрдВрдЯ рдХреЗ рдмреИрдХрдПрдВрдб рд╕реЗ рдХрдиреЗрдХреНрдЯ рд╣реЛрддрд╛ рд╣реИ
  • тЬЕ рдЬрд╝реНрдпрд╛рджрд╛ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛рдУрдВ рдХреЛ рд╣реИрдВрдбрд▓ рдХрд░рдиреЗ рд╡рд╛рд▓реЗ, рдмрд┐рдирд╛ рдХрд┐рд╕реА рд░реБрдХрд╛рд╡рдЯ рдХреЗ рд╣реЛрд╕реНрдЯ рдХрд┐рдП рдЬрд╛ рд╕рдХрдиреЗ рд╡рд╛рд▓реЗ, рдФрд░ рдмрд┐рдирд╛ рд╕рд░реНрд╡рд░ рдХреЗ рдХрд╛рдо рдХрд░рдиреЗ рд╡рд╛рд▓реЗ рдРрдкреНрд▓рд┐рдХреЗрд╢рди рдХреЗ рд▓рд┐рдП, Cloud Run рдбрд┐рдкреНрд▓реЙрдпрдореЗрдВрдЯ

рдЦрд╛рд╕ рдХреЙрдиреНрд╕реЗрдкреНрдЯ

рд╕рд┐рджреНрдзрд╛рдВрдд

рдЖрдкрдиреЗ рдЗрд╕реЗ рдХрд╣рд╛рдВ рджреЗрдЦрд╛

ADK рдХреА рдорджрдж рд╕реЗ рдорд▓реНрдЯреА-рдПрдЬреЗрдВрдЯ рдСрд░реНрдХреЗрд╕реНрдЯреНрд░реЗрд╢рди

рд░реВрдЯ рдПрдЬреЗрдВрдЯ, рдлрд╝рд┐рдЯрд┐рдВрдЧ рд░реВрдо, рдХреИрдЯрд▓реЙрдЧ, рдФрд░ рд╕реНрдЯрд╛рдЗрд▓рд┐рд╕реНрдЯ рдПрдЬреЗрдВрдЯ рдХреЛ рд░реВрдЯ рдХрд░рддрд╛ рд╣реИ

Gemini рдХреА рдорд▓реНрдЯреАрдореЙрдбрд▓ рдЗрдореЗрдЬ рдЬрдирд░реЗрдЯ рдХрд░рдиреЗ рдХреА рд╕реБрд╡рд┐рдзрд╛

fitting_tool рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреА рдлрд╝реЛрдЯреЛ рдХреЛ рдкреНрд░реЙрдбрдХреНрдЯ рдХреА рдЗрдореЗрдЬ рдХреЗ рд╕рд╛рде рдЬреЛрдбрд╝рдирд╛

рдмрд╛рддрдЪреАрдд рдХрд░рдиреЗ рд╡рд╛рд▓реЗ рдПрдЖрдИ рдХреЗ рд▓рд┐рдП рд╕реЗрд╢рди рдХреА рд╕реНрдерд┐рддрд┐

рд╕реНрдЯрд╛рдЗрд▓рд┐рд╢, рдмрд╛рд░-рдмрд╛рд░ рдорд┐рд▓рдиреЗ рд╡рд╛рд▓реЗ рдлрд╝реАрдбрдмреИрдХ рдХреЗ рд▓рд┐рдП рд╕реЗрд╢рди рдХрд╛ рдлрд┐рд░ рд╕реЗ рдЗрд╕реНрддреЗрдорд╛рд▓ рдХрд░ рд░рд╣рд╛ рд╣реИ

рдмрд╛рдЗрдирд░реА рдбреЗрдЯрд╛ рдХреЗ рд▓рд┐рдП рдЖрд░реНрдЯрдлрд╝реИрдХреНрдЯ рд╕реНрдЯреЛрд░реЗрдЬ

рдЗрдореЗрдЬ рд╕реНрдЯреЛрд░реЗрдЬ рдХреЛ рдЯреЗрдХреНрд╕реНрдЯ рд╡рд╛рд▓реЗ рдЬрд╡рд╛рдмреЛрдВ рд╕реЗ рдЕрд▓рдЧ рдХрд░рдирд╛

рдорд┐рдбрд▓рд╡реЗрдпрд░ рд▓реЙрдЬрд┐рдХ рдХреЗ рд▓рд┐рдП рдХреЙрд▓рдмреИрдХ

SaveIncomingBlobs, InjectPreviousProducts, SaveSelectedProducts

Flutter рдореЗрдВ MVVM + Provider

ChangeNotifier рдХреЗ рд╕рд╛рде TryItOnProvider рдФрд░ StylingProvider

рдПрдЬреЗрдВрдЯрд┐рдХ рд╣реИрдВрдбрдСрдлрд╝

StyleRequest рдПрдЬреЗрдВрдЯреЛрдВ рдХреЗ рдмреАрдЪ рдорд▓реНрдЯреАрдореЙрдбрд▓ рдХреЙрдиреНрдЯреЗрдХреНрд╕реНрдЯ рдкрд╛рд╕ рдХрд░рдирд╛

рдЕрдЧрд▓реЗ рдЪрд░рдг

  • ЁЯОи рдПрдЬреЗрдВрдЯ рдХреЗ рдкреНрд░реЙрдореНрдкреНрдЯ рдХреЛ рдкрд╕рдВрдж рдХреЗ рдореБрддрд╛рдмрд┐рдХ рдмрдирд╛рдПрдВ тАФ рд╕реНрдЯрд╛рдЗрд▓рд┐рд╕реНрдЯ рдХреА рдкрд░реНрд╕рдиреИрд▓рд┐рдЯреА рдмрджрд▓рдиреЗ рдХреЗ рд▓рд┐рдП, instructions.md рдореЗрдВ рдмрджрд▓рд╛рд╡ рдХрд░реЗрдВ
  • ЁЯЫНя╕П рдЬрд╝реНрдпрд╛рджрд╛ рдкреНрд░реЙрдбрдХреНрдЯ рдЬреЛрдбрд╝реЗрдВ тАФ catalog.yaml рдХреЛ рдирдП рдЖрдЗрдЯрдо рдХреЗ рд╕рд╛рде рдЕрдкрдбреЗрдЯ рдХрд░реЗрдВ
  • ЁЯУ▒ рдореЛрдмрд╛рдЗрд▓ рдХреЗ рд▓рд┐рдП рдмрдирд╛рдПрдВ тАФ flutter build ios рдпрд╛ flutter build apk рдЪрд▓рд╛рдПрдВ
  • ЁЯФД рдкрд░рд╕рд┐рд╕реНрдЯреЗрдВрдЯ рд╕реЗрд╢рди рдЬреЛрдбрд╝рдирд╛ тАФ InMemoryService рдХреЛ рдбреЗрдЯрд╛рдмреЗрд╕ рд╕реЗ рдмреИрдХрдЕрдк рд▓рд┐рдП рдЧрдП рдЗрдВрдкреНрд▓рд┐рдореЗрдВрдЯреЗрд╢рди рд╕реЗ рдмрджрд▓реЗрдВ
  • ЁЯФТ рдкреБрд╖реНрдЯрд┐ рдХрд░рдиреЗ рдХреА рд╕реБрд╡рд┐рдзрд╛ рдЬреЛрдбрд╝рдирд╛ тАФ IAM рдХреА рдорджрдж рд╕реЗ Cloud Run рдПрдВрдбрдкреЙрдЗрдВрдЯ рдХреЛ рд╕реБрд░рдХреНрд╖рд┐рдд рдХрд░рдирд╛

рд╕рдВрд╕рд╛рдзрди