Files
2026-07-13 12:37:47 +08:00

98 lines
3.4 KiB
Python

import nest_asyncio
from llama_index.llms.ollama import Ollama
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
from llama_index.core.settings import Settings
from llama_index.core.workflow import Event, Context, Workflow, StartEvent, StopEvent, step
from llama_index.core.schema import NodeWithScore
from llama_index.core import SimpleDirectoryReader, VectorStoreIndex
from llama_index.core.response_synthesizers import CompactAndRefine
# Apply nest_asyncio to allow nested event loops
nest_asyncio.apply()
class RetrieverEvent(Event):
"""Result of running retrieval"""
nodes: list[NodeWithScore]
class RAGWorkflow(Workflow):
def __init__(self, model_name="llama3.2", embedding_model="BAAI/bge-small-en-v1.5"):
super().__init__()
# Initialize LLM and embedding model
self.llm = Ollama(model=model_name)
self.embed_model = HuggingFaceEmbedding(model_name=embedding_model)
# Configure global settings
Settings.llm = self.llm
Settings.embed_model = self.embed_model
self.index = None
@step
async def ingest(self, ctx: Context, ev: StartEvent) -> StopEvent | None:
"""Entry point to ingest documents from a directory."""
dirname = ev.get("dirname")
if not dirname:
return None
documents = SimpleDirectoryReader(dirname).load_data()
self.index = VectorStoreIndex.from_documents(documents=documents)
return StopEvent(result=self.index)
@step
async def retrieve(self, ctx: Context, ev: StartEvent) -> RetrieverEvent | None:
"""Entry point for RAG retrieval."""
query = ev.get("query")
index = ev.get("index") or self.index
if not query:
return None
if index is None:
print("Index is empty, load some documents before querying!")
return None
retriever = index.as_retriever(similarity_top_k=2)
nodes = await retriever.aretrieve(query)
await ctx.set("query", query)
return RetrieverEvent(nodes=nodes)
@step
async def synthesize(self, ctx: Context, ev: RetrieverEvent) -> StopEvent:
"""Generate a response using retrieved nodes."""
summarizer = CompactAndRefine(streaming=True, verbose=True)
query = await ctx.get("query", default=None)
response = await summarizer.asynthesize(query, nodes=ev.nodes)
return StopEvent(result=response)
async def query(self, query_text: str):
"""Helper method to perform a complete RAG query."""
if self.index is None:
raise ValueError("No documents have been ingested. Call ingest_documents first.")
result = await self.run(query=query_text, index=self.index)
return result
async def ingest_documents(self, directory: str):
"""Helper method to ingest documents."""
result = await self.run(dirname=directory)
self.index = result
return result
# Example usage
async def main():
# Initialize the workflow
workflow = RAGWorkflow()
# Ingest documents
await workflow.ingest_documents("data")
# Perform a query
result = await workflow.query("How was DeepSeekR1 trained?")
# Print the response
async for chunk in result.async_response_gen():
print(chunk, end="", flush=True)
if __name__ == "__main__":
import asyncio
asyncio.run(main())