diff --git a/offline-backend-20260512/backend/packages/harness/deerflow/knowledge/service.py b/offline-backend-20260512/backend/packages/harness/deerflow/knowledge/service.py index 78351be6..7d41ae9b 100644 --- a/offline-backend-20260512/backend/packages/harness/deerflow/knowledge/service.py +++ b/offline-backend-20260512/backend/packages/harness/deerflow/knowledge/service.py @@ -228,11 +228,22 @@ class KnowledgeService: Returns ``{"note": , "created": bool}``. Raises :class:`EmptyThreadError` when there is nothing worth keeping. """ + # Always-on entry log: proves a capture was triggered for this thread, + # regardless of how it ends (success / skip / fallback). Lets you tell + # "capture never ran" apart from "logs not visible" via one grep. + logger.info( + "[knowledge] capturing thread %s (msgs=%d, llm_extract=%s, skip_existing=%s)", + thread_id, + len(messages or []), + self.config.llm_extract_enabled, + skip_existing, + ) if skip_existing: existing = await self.repo.find_by_source("thread", thread_id) if existing is None and self.adapter.manifest.has_source(f"thread:{thread_id}"): existing = await self.repo.find_by_source("thread", thread_id) if existing: + logger.info("[knowledge] thread %s already captured (note %s); skipping", thread_id, existing.get("id")) return {"note": existing, "created": False} sources = [] @@ -286,6 +297,7 @@ class KnowledgeService: ) drafts = [single] if single else [] if not drafts: + logger.info("[knowledge] thread %s has no sediment-able content; skipping", thread_id) raise EmptyThreadError(f"Thread {thread_id} has no sediment-able content") # When the LLM was enabled but silently fell back to rule-based extraction, @@ -318,6 +330,14 @@ class KnowledgeService: draft.related = list(dict.fromkeys([*siblings, *draft.related])) notes = [await self._persist_draft(draft, created_by=created_by) for draft in drafts] + logger.info( + "[knowledge] thread %s captured → %d note(s) [%s], status=%s, llm_fallback=%s", + thread_id, + len(notes), + ", ".join(n.get("title", "") for n in notes), + normalized, + llm_fallback, + ) return {"note": notes[0], "created": True, "notes": notes, "llm_fallback": llm_fallback} async def capture_search(