ingest whole textbooks chapter by chapter, resumably
نقشهی فیزیکیِ کتابها (افستِ صفحه، بازهی هر فصل، درسهای داخلِ فصل از فهرستِ خودِ کتاب) در rag/services/textbooks.py ثبت شد تا عنوانِ فصل از کتاب بیاید نه از حدسِ مدل — رشتهی فصل باید دقیقاً بینِ ingest و retrieval یکی باشد وگرنه نتیجه بیسروصدا خالی برمیگردد. rag_ingest_book کتاب را فصلبهفصل ingest میکند و قابلِ ازسرگیری است: rag.IngestedPage نشانهی «این صفحه پردازش شد» را نگه میدارد حتی وقتی صفحه chunkی نساخته، وگرنه هر بار اجرای دوباره بابتِ صفحههای بیمسئله دوباره پول میگرفت. --plan هزینه را رایگان برآورد میکند. rag_import_collection نقاطِ موجودِ Qdrant را در LessonChunk میریزد — فقط میخواند، رایگان و idempotent، برای پایههایی که تیمِ دیگر ingest کرده بود. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -18,7 +18,9 @@ class LessonChunk(models.Model):
|
||||
دوبارهی embedding قابلِ بازسازی باشه.
|
||||
"""
|
||||
|
||||
lesson_id = models.IntegerField(db_index=True) # ارجاع سبک به content.Lesson
|
||||
# ارجاع سبک به content.Lesson. برای ingestِ کتابِ کامل خالی میماند: آن
|
||||
# مسیر فصلبهفصل کار میکند و هنوز رکوردِ Lessonی برایش وجود ندارد.
|
||||
lesson_id = models.IntegerField(db_index=True, null=True, blank=True)
|
||||
grade_id = models.IntegerField(null=True, blank=True) # ارجاع سبک به content.Grade
|
||||
subject = models.CharField(max_length=100)
|
||||
lesson_number = models.IntegerField()
|
||||
@@ -46,6 +48,11 @@ class LessonChunk(models.Model):
|
||||
problem = models.TextField(blank=True)
|
||||
solution = models.TextField(blank=True)
|
||||
|
||||
# شمارهی صفحهی PDF که این chunk از آن درآمده. ingestِ کتاب با همین فیلد
|
||||
# میفهمد کدام صفحهها قبلاً پردازش شدهاند و از سر میگیرد؛ بدونش هر
|
||||
# اجرای دوباره پس از قطعی، نقاطِ تکراری در Qdrant میساخت.
|
||||
source_page = models.PositiveIntegerField(null=True, blank=True, db_index=True)
|
||||
|
||||
collection = models.CharField(max_length=200, db_index=True)
|
||||
qdrant_point_id = models.UUIDField(default=uuid.uuid4, editable=False)
|
||||
|
||||
@@ -61,6 +68,7 @@ class LessonChunk(models.Model):
|
||||
]
|
||||
indexes = [
|
||||
models.Index(fields=['subject', 'lesson_number']),
|
||||
models.Index(fields=['collection', 'chapter_title', 'source_page']),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
@@ -77,3 +85,32 @@ class LessonChunkImage(models.Model):
|
||||
|
||||
def __str__(self):
|
||||
return f"image-chunk{self.chunk_id}"
|
||||
|
||||
|
||||
class IngestedPage(models.Model):
|
||||
"""
|
||||
نشانهی «این صفحهی کتاب پردازش شد».
|
||||
|
||||
چرا جدا از LessonChunk: صفحههایی که هیچ مسئلهای ندارند (جلد، فهرست،
|
||||
سخنی با معلم، صفحهی جداکنندهی فصل) هیچ chunkی نمیسازند. اگر ازسرگیری
|
||||
فقط به chunkها نگاه کند، این صفحهها در هر اجرای دوباره یک فراخوانیِ
|
||||
vision دیگر خرج میکنند و هیچوقت هم «تمام» نمیشوند.
|
||||
"""
|
||||
|
||||
collection = models.CharField(max_length=200, db_index=True)
|
||||
chapter_title = models.CharField(max_length=255)
|
||||
page = models.PositiveIntegerField()
|
||||
chunk_count = models.PositiveSmallIntegerField(default=0)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
class Meta:
|
||||
constraints = [
|
||||
models.UniqueConstraint(
|
||||
fields=['collection', 'page'],
|
||||
name='uniq_ingested_page_per_collection',
|
||||
),
|
||||
]
|
||||
ordering = ['collection', 'page']
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.collection} p{self.page} ({self.chunk_count} chunk)"
|
||||
|
||||
Reference in New Issue
Block a user