Skip to content
كل المقالات

إزاي Gym Buddy بيحوّل صورة أكلك لسعرات

بنصغّر الصورة على الموبايل، ونطلب من Gemini بيانات بشكل ثابت، ونتعامل مع خطة مجانية أحياناً بتبقى مشغولة.

أحمد عبد المجيد

قراءة دقيقتين

شارك:WhatsAppLinkedIn

تسجيل الأكل هو الجزء اللي الناس بتزهق منه في تطبيقات اللياقة. حل Gym Buddy زرار كاميرا: تصوّر طبقك، ويرجعلك كل صنف وكميته وسعراته والماكروز، وتقدر تعدّل أي رقم قبل الحفظ. ده الشرح من الأول للآخر، على الخطة المجانية من Google Gemini.

نتيجة المسح في Gym Buddy لطبق كشري: 755 سعرة مع البروتين والكربوهيدرات والدهون
طبق كشري: 755 سعرة، والماكروز، ودقة التقدير، وملاحظة إن الكمية اتقدّرت من حجم الطبق. وكل رقم تقدر تعدّله قبل الحفظ.

١. نصغّر الصورة على الموبايل#

صورة الموبايل بتبقى كذا ميجا. الموديل مش محتاج كل التفاصيل دي عشان يفرّق بين الكشري والفول، فالتطبيق بيصغّر الصورة لعرض 1024 بكسل ويحفظها JPEG بجودة 70% قبل الرفع. الرفع بيبقى أسرع على داتا الموبايل، وكل طلب بيفضل أقل بكتير من حد السيرفر اللي هو 15 ميجا.

async function toUploadableJpeg(uri: string, width = 1024) {
  const image = await ImageManipulator.manipulate(uri).resize({ width }).renderAsync();
  const result = await image.saveAsync({ base64: true, compress: 0.7, format: SaveFormat.JPEG });
  return result.base64;
}

٢. نطلب بيانات مش كلام#

السيرفر بيبعت الصورة لـ Gemini مع تعليمات مكتوبة كأنها لأخصائي تغذية رياضية: حدّد كل صنف، قدّر الكمية من العلامات اللي في الصورة زي حجم الطبق والمعالق والإيد، ولو فيه جدول قيم غذائية باين استخدم أرقامه، واعرف الأكلات المصرية بأساميها.

أهم جزء هو شكل الرد. بدل ما نحلّل نص حر، الطلب نفسه فيه JSON schema متولّد من نفس مخطط Zod اللي السيرفر بيتحقق بيه، فالموديل مضطر يرد بنفس الشكل بالظبط.

const FoodAnalysisSchema = z.object({
  isFood: z.boolean(),
  mealName: z.string(),
  items: z.array(z.object({
    name: z.string(), portion: z.string(),
    calories: z.number(), protein: z.number(), carbs: z.number(), fat: z.number(),
  })),
  // ...
});

config: { systemInstruction: SYSTEM_PROMPT, responseMimeType: "application/json", responseJsonSchema }

وبرضه الرد بيعدّي على safeParse قبل ما يوصل للتطبيق. الـ schema في الطلب بتخلّي الشكل الصح شبه مضمون، والتحقق من الرد بيخلّيه مضمون فعلاً.

الـ schema في الطلب بتخلّي الشكل الصح شبه مضمون، والتحقق من الرد بيخلّيه مضمون فعلاً.

٣. نقول لأ للحاجات اللي مش أكل#

الناس هتوجّه الكاميرا على القطة بتاعتهم. الـ schema فيها علامة isFood، والتعليمات بتقول لو مفيش أكل أو مشروب خلّيها false ورجّع قايمة فاضية، فالتطبيق يعرض رسالة لطيفة بدل ما يخترع قطة فيها 300 سعر.

٤. نعتبر أوقات زحمة الخطة المجانية حاجة عادية#

الخطة المجانية من Gemini أحياناً بترد بـ 503 "ضغط عالي" لثواني. الكود بيعيد المحاولة لحد 3 مرات مع انتظار قصير للأخطاء 500 و502 و503 و504. وبشكل مقصود مش بيعيد مع 429: لما الحصة تخلص، الانتظار ثانية كمان مش هيرجّعها، فالمستخدم بيشوف رسالة واضحة "جرّب تاني بعد دقيقة".

new GoogleGenAI({
  apiKey: process.env.GEMINI_API_KEY,
  httpOptions: { retryOptions: { attempts: 3, initialDelay: 1, maxDelay: 4, httpStatusCodes: [500, 502, 503, 504] } },
});

كل خطأ من Gemini بيتحوّل لجملة المستخدم يقدر يتصرف بيها: المفتاح مش موجود، الحصة خلصت، الذكاء الاصطناعي مشغول، أو الصورة مش قابلة للمعالجة. ولا واحد فيهم بيظهر كرقم خطأ خام.

نصيحتي لأي حد بيبني حاجة زي كده#

  • صغّر الصورة قبل الرفع، التعرّف على الأكل مش محتاج الدقة الكاملة.
  • حط الـ schema في الطلب واتحقق من الرد برضه.
  • ادّي الموديل طريقة يقول بيها "ده مش أكل".
  • أعد المحاولة مع الأعطال المؤقتة، ومتعيدش أبداً لما الحصة تخلص، واكتب كل خطأ كنصيحة.
شارك:WhatsAppLinkedIn

Keyboard shortcuts