ContiAI / agents /books /book_structure.py
alaa wael
alaa update project
d5c9e32
Raw
History Blame Contribute Delete
21 kB
# from crewai import Agent, Task
# import os
# import json
# from modules import llm_4m
# from schemas import BookOutlineOutput
# # ---------------------------------
# # 2) Book Structure Agent
# # ---------------------------------
# def book_structure_agent():
# model = llm_4m()
# return Agent(
# name="SubsectionStructureAgent",
# role="منظّم محاور فرعية داخل هيكل كتاب جاهز",
# goal=(
# "تنظيم وبناء المحاور الفرعية (Subsections) فقط داخل الهيكل المُعطى للكتاب، "
# "مع الحفاظ التام على أسماء وترتيب الفصول والأقسام كما وردت، "
# "ودون إنشاء أو إعادة تسمية أي وحدات عليا (Chapters/Sections)، "
# "ودون كتابة محتوى فعلي. "
# "المطلوب: محاور فرعية بعناوين جذابة وواضحة وغير تقليدية، تُسهل رحلة القارئ."
# ),
# backstory=(
# "أنت محرر كتب محترف متخصص في تصميم هياكل الكتب بمختلف أنواعها "
# "(تعليمية، معرفية، تطوير ذات، أعمال، أو غير خيالية). "
# "تجيد العمل داخل هياكل كتب جاهزة دون تعديلها، "
# "وتلتزم التزامًا صارمًا بالحفاظ على أسماء الفصول والأقسام وترتيبها كما هي. "
# "ويقتصر دورك على بناء المحاور الفرعية (Subsections) وتنظيمها منطقيًا داخل كل قسم.\n\n"
# "تميزك الأساسي: صياغة عناوين محاور فرعية قوية غير جامدة ولا أكاديمية ثقيلة. "
# "تتجنب التكرار اللفظي، وتبتعد عن الافتتاحيات التقليدية، "
# "وتقسم الأفكار الكبيرة إلى محاور صغيرة واضحة (فكرة واحدة لكل محور)."
# ),
# llm=model,
# verbose=False,
# memory=False,
# allow_delegation=False,
# )
# # ---------------------------------
# # 3) Task: Generate Book Outline JSON (STRICT)
# # ---------------------------------
# def generate_book_outline_task(structure_agent):
# return Task(
# name="Generate book outline (NO content writing)",
# description=(
# """أنت تبني هيكل كتاب فقط (Outline) من بيانات الإدخال،
# بما يتناسب مع نوع الكتاب (تعليمي، معرفي، تطوير ذات، أعمال، أو غير خيالي)،
# مع مراعاة الجمهور المستهدف وطبيعة التجربة القرائية.
# """
# """تنبيه صارم:
# - لا تقم بإنشاء فصول (Chapters) جديدة.
# - لا تقم بإنشاء أقسام (Sections) جديدة.
# - لا تغيّر أسماء Chapters أو Sections الواردة في raw_units حرفيًا.
# - لا تعيد ترتيب الفصول أو الأقسام.
# - دورك يقتصر حصريًا على:
# • تنظيم وبناء Subsections داخل كل Section موجود.
# """
# "==============================\n"
# "قواعد إلزامية (معايير إعداد الكتب)\n"
# "==============================\n"
# "1) ممنوع كتابة محتوى الكتاب/شرح مطول/أمثلة تفصيلية. أنت تنتج هيكل فقط.\n"
# "2) الوضوح والبساطة في العناوين واللغة.\n"
# """3) عدد Subsections يجب أن يكون مرنًا حسب حجم الفكرة داخل Section.
# - الحد الأدنى: 2
# - الحد الأقصى: 6
# لكن لا تختَر دائمًا الحد الأدنى.
# قاعدة الاختيار:
# - إذا كان القسم بسيط → 2–3 محاور
# - إذا كان القسم متوسط → 3–4 محاور
# - إذا كان القسم غني بالمفاهيم → 4–6 محاور
# اختر العدد الذي يخدم الفكرة وليس الحد الأدنى دائمًا."""
# "5) منع التكرار: لا تكرر نفس الفكرة بين محاور متجاورة، ولا تكرر نفس الصياغة الافتتاحية.\n"
# """6) التدرج:
# - الكتب التعليمية: من الأساسيات إلى التطبيق/التحليل
# - الكتب غير التعليمية: من التمهيد إلى التعمق ثم الخلاصة/التحول
# - يجب أن يعكس الترتيب رحلة القارئ الطبيعية
# """
# "7) ترابط منطقي بين الأقسام والمحاور (بدون قفزات مفاهيمية).\n\n"
# "==============================\n"
# "قواعد إلزامية لصياغة عناوين Subsections (مهم جدًا)\n"
# "==============================\n"
# "A) عناوين المحاور الفرعية لازم تكون:\n"
# " - قصيرة وواضحة (يفضل 2–6 كلمات).\n"
# " - غير تقليدية/غير جامدة، وتشد القارئ.\n"
# " - لغة بسيطة مناسبة للجمهور (ابتعد عن الأسلوب الأكاديمي الثقيل).\n"
# " - فكرة واحدة لكل عنوان.\n"
# "B) ممنوع تكرار بدايات العناوين أو الحشو اللفظي.\n"
# " - لا تبدأ عدة محاور بنفس الصيغة مثل: (في ظل...)، (يبرز...)، (من المعروف...)، (يتناول هذا القسم...).\n"
# "C) تجنب العناوين المركّبة التي تجمع محورين مختلفين في عنوان واحد.\n"
# " - مثال ممنوع: (تعريف الصمود النفسي وأهميته)\n"
# " - الصحيح: محور لتعريف المفهوم + محور مستقل للأهمية/الفوائد.\n"
# "D) تجنب كلمات فضفاضة أو غير دقيقة في العناوين مثل:\n"
# " - (الرئيسية/الأساسية) إذا كانت لا تضيف معنى.\n"
# "E) لا تستخدم عناوين تشبه عناوين الأبحاث (توثيق/اقتباسات/أسماء باحثين/سنوات) داخل العناوين.\n"
# "F) يجوز استخدام أساليب عنوان جذابة حسب نوع القسم:\n"
# " - سؤال تحفيزي، عبارة قصيرة، أو صياغة عملية (لكن بدون كتابة محتوى).\n\n"
# """
# ==============================
# تقسيم المحاور بطريقة جذابة للقارئ
# ==============================
# لا تقسّم المحاور بطريقة ميكانيكية.
# بدلًا من تقسيم عشوائي، استخدم أحد أنماط التقسيم التالية حسب طبيعة الموضوع:
# - المشكلة → الفهم → الحل
# - المفهوم → المكونات → التطبيق
# - الفكرة → التحديات → الاستراتيجيات
# - الأساسيات → الأدوات → الاستخدام
# - الأخطاء → التصحيح → التحسين
# الهدف:
# أن يشعر القارئ أن كل Section يمثل رحلة فكرية وليس مجرد عناوين متفرقة.
# """
# """
# ==============================
# توزيع الصفحات (مهم جدًا)
# ==============================
# إذا تم تمرير target_total_pages:
# 1) احسب متوسط صفحات الفصل:
# avg_chapter_pages = target_total_pages ÷ عدد الفصول
# مثال:
# كتاب 100 صفحة
# 4 فصول
# → متوسط الفصل ≈ 25 صفحة
# 2) وزّع الصفحات على الفصول بشكل قريب من المتوسط
# لكن يمكن أن يختلف ±20% حسب عمق الفصل.
# 3) داخل كل فصل:
# قسّم الصفحات على Sections بشكل منطقي.
# 4) داخل كل Section:
# قسّم الصفحات على Subsections بحيث:
# - المحور القصير: 1–2 صفحات
# - المحور المتوسط: 2–3 صفحات
# - المحور المتعمق: 3–5 صفحات
# 5) تحقق إلزامي:
# مجموع est_pages لكل Subsections
# = target_total_pages بالضبط
# إذا لم يساوِ:
# قم بتعديل التوزيع حتى يتطابق المجموع.
# """
# """
# ==============================
# مراعاة حجم الكتاب
# ==============================
# لا تقرر عدد المحاور أولًا.
# ابدأ بتقدير حجم الكتاب:
# - كتاب 60–100 صفحة → محاور أقل وشرح مركز
# - كتاب 100–180 صفحة → توازن بين عدد المحاور وعمق الشرح
# - كتاب 180+ صفحة → محاور أكثر وتفصيل أكبر
# القاعدة الأساسية:
# عدد الصفحات هو الذي يحدد عدد المحاور
# وليس العكس.
# """
# "==============================\n"
# "الحفاظ على الأسماء\n"
# "==============================\n"
# """- استخدم عناوين Chapters وSections كما هي حرفيًا من raw_units.
# - يُمنع إعادة الصياغة أو التحسين اللغوي لعناوين Chapters/Sections.
# - التعديل/التحسين مسموح فقط داخل Subsections التي تنشئها.
# """
# "==============================\n"
# "المدخلات\n"
# "==============================\n"
# "- book_title: {book_title}\n"
# "- target_audience: {target_audience}\n"
# "- book_description: {book_description}\n"
# "- learning_outcomes: {learning_outcomes}\n"
# "- raw_units: {raw_units}\n"
# "- target_total_pages (optional): {target_total_pages}\n"
# "- example_reference (optional): {example_reference}\n\n"
# """ملاحظة مهمة:
# raw_units تمثل الهيكل النهائي للفصول والأقسام.
# يُمنع تعديل أو إعادة تسمية أي Unit أو Topic.
# Subsections فقط هي التي يتم إنشاؤها وتنظيمها.
# """
# "==============================\n"
# "مطلوب الإخراج (JSON صارم)\n"
# "==============================\n"
# "أخرج JSON فقط مطابق لـ BookOutlineOutput وبنفس أسماء الحقول:\n"
# "{\n"
# ' "book_title": "...",\n'
# ' "target_audience": "...",\n'
# ' "book_description": "...",\n'
# ' "chapters": [\n'
# " {\n"
# ' "chapter_title": "...",\n'
# ' "outcome": "...",\n'
# ' "sections": [\n'
# " {\n"
# ' "title": "...",\n'
# ' "overview": "...",\n'
# ' "subsections": [\n'
# " {\n"
# ' "title": "...",\n'
# ' "purpose": "...",\n'
# ' "suggested_queries": ["..."],\n'
# ' "difficulty": "basic|intermediate|advanced",\n'
# ' "est_pages": 1\n'
# " }\n"
# " ]\n"
# " }\n"
# " ]\n"
# " }\n"
# " ],\n"
# ' "outline_notes": "..."\n'
# ' "total_pages": 50\n'
# "}\n"
# "==============================\n"
# "تحقق إلزامي قبل الإخراج\n"
# "==============================\n"
# """
# قبل إخراج الـ JSON النهائي، راجع ما يلي بدقة:
# - كل Chapter يحتوي 2–6 Sections
# - كل Section يحتوي على 2–6 Subsections (لا أقل من 2 نهائيًا)
# - إذا وجدت Section تحتوي فكرة واحدة فقط:
# → قم بتقسيمها إلى محورين منطقيين مختلفين
# - راجع عناوين Subsections:
# → لا تكرار في بدايات العناوين
# → لا عناوين جامدة/تقليدية
# → لا دمج (تعريف + أهمية) في عنوان واحد
# 1) اجمع جميع est_pages في Subsections.
# 2) تحقق أن المجموع = target_total_pages.
# إذا لم يتطابق:
# قم بإعادة توزيع est_pages حتى يصبح المجموع مطابقًا تمامًا.
# - لا تخرج JSON حتى تتأكد أن جميع القيود محققة
# - أي مخالفة ستُعد إخراجًا غير صالح
# """
# ),
# expected_output="Strict JSON matching BookOutlineOutput schema (NO extra keys).",
# output_json=BookOutlineOutput,
# agent=structure_agent,
# async_execution=False,
# )
from crewai import Agent, Task
from modules import llm_open_4m
from schemas import BookOutlineOutput
# ---------------------------------------------------
# Agent: Book Structure Designer
# ---------------------------------------------------
def book_structure_agent():
model = llm_open_4m()
return Agent(
name="BookStructureDesigner",
role="مصمم هياكل كتب احترافية",
goal=(
"تصميم محاور فرعية جذابة داخل الهيكل المعطى للكتاب "
"مع الحفاظ الكامل على أسماء الفصول والأقسام كما هي "
"ودون تعديلها أو إعادة ترتيبها."
),
backstory=(
"أنت محرر كتب محترف تعمل مع دور نشر كبيرة. "
"مهمتك تصميم المحاور الفرعية (Subsections) التي تجعل القارئ "
"يتنقل داخل القسم بسهولة وبفضول معرفي.\n\n"
"لا تكتب محتوى الكتاب. دورك هو تصميم الهيكل فقط.\n\n"
"تميزك الأساسي:\n"
"- صياغة عناوين جذابة وليست أكاديمية ثقيلة\n"
"- تقسيم الفكرة الكبيرة إلى محاور واضحة\n"
"- كل عنوان يمثل فكرة واحدة فقط\n"
"- تجنب التكرار والجمود في العناوين\n"
),
llm=model,
verbose=False,
memory=False,
allow_delegation=False,
)
# ---------------------------------------------------
# Task: Generate Book Outline
# ---------------------------------------------------
def generate_book_outline_task(structure_agent):
return Task(
name="Generate book outline",
description=(
"مهمتك بناء هيكل كتاب (Book Outline) فقط.\n"
"لا تكتب محتوى فعلي للكتاب.\n\n"
"=========================\n"
"القواعد الأساسية\n"
"=========================\n"
"1) لا تقم بإنشاء Chapters جديدة\n"
"2) لا تقم بإنشاء Sections جديدة\n"
"3) لا تغيّر أسماء Chapters أو Sections\n"
"4) لا تعيد ترتيب الهيكل\n"
"5) دورك فقط إنشاء Subsections داخل كل Section\n\n"
"=========================\n"
"قواعد إنشاء Subsections\n"
"=========================\n"
"- كل Section يجب أن يحتوي 2 إلى 6 Subsections\n"
"- كل Subsection يمثل فكرة واحدة واضحة\n"
"- رتب المحاور بشكل منطقي بحيث تشكل رحلة للقارئ\n"
"أنماط تقسيم مناسبة:\n"
"- المفهوم → الفهم → التطبيق\n"
"- المشكلة → التحليل → الحل\n"
"- الفكرة → التحديات → الاستراتيجيات\n"
"- الأساسيات → الأدوات → الاستخدام\n\n"
"=========================\n"
"قواعد صياغة العناوين\n"
"=========================\n"
"العناوين يجب أن تكون:\n"
"- قصيرة (2 إلى 6 كلمات)\n"
"- جذابة وسهلة القراءة\n"
"- غير أكاديمية ثقيلة\n"
"- فكرة واحدة لكل عنوان\n"
"تجنب:\n"
"- تعريف + أهمية في نفس العنوان\n"
"- تكرار نفس بداية العنوان\n"
"- العناوين الجامدة\n\n"
"أمثلة لعناوين جيدة:\n"
"- لماذا تفشل الخطط؟\n"
"- لحظة اكتشاف المشكلة\n"
"- الفكرة التي تغير كل شيء\n"
"- عندما يتحول الضغط إلى فرصة\n\n"
"=========================\n"
"المدخلات\n"
"=========================\n"
"book_title: {book_title}\n"
"target_audience: {target_audience}\n"
"book_description: {book_description}\n"
"learning_outcomes: {learning_outcomes}\n"
"raw_units: {raw_units}\n"
"target_total_pages: {target_total_pages}\n\n"
"example_reference (استلهم منه أسلوب العناوين فقط):\n"
"{example_reference}\n\n"
"raw_units تمثل الهيكل النهائي للفصول والأقسام.\n"
"يُمنع تعديلها.\n"
"قم فقط بإضافة Subsections داخل كل Section.\n\n"
"=========================\n"
"توزيع الصفحات\n"
"=========================\n"
"إذا تم تمرير target_total_pages:\n"
"- وزع الصفحات على Subsections\n"
"- المحور القصير: 1-2 صفحات\n"
"- المحور المتوسط: 2-3 صفحات\n"
"- المحور العميق: 3-5 صفحات\n"
"مجموع الصفحات يجب أن يساوي target_total_pages.\n\n"
"=========================\n"
"الإخراج المطلوب\n"
"=========================\n"
"أخرج JSON فقط مطابق لـ BookOutlineOutput.\n"
"{\n"
' "book_title": "...",\n'
' "target_audience": "...",\n'
' "book_description": "...",\n'
' "chapters": [\n'
" {\n"
' "chapter_title": "...",\n'
' "outcome": "...",\n'
' "sections": [\n'
" {\n"
' "title": "...",\n'
' "overview": "...",\n'
' "subsections": [\n'
" {\n"
' "title": "...",\n'
' "purpose": "...",\n'
' "suggested_queries": ["..."],\n'
' "difficulty": "basic|intermediate|advanced",\n'
' "est_pages": 1\n'
" }\n"
" ]\n"
" }\n"
" ]\n"
" }\n"
" ],\n"
' "outline_notes": "...",\n'
' "total_pages": 50\n'
"}\n\n"
"لا تضف أي نص خارج JSON."
),
expected_output="Strict JSON matching BookOutlineOutput schema",
output_json=BookOutlineOutput,
agent=structure_agent,
async_execution=False,
)