mirror of
https://github.com/magnus919/agent-skills.git
synced 2026-09-20 16:16:25 +03:00
Greenfield SkillOpt: 3 epochs optimizing discoverability, decision guidance, and pattern expansion for a brand-new LlamaIndex skill. Epoch 1 — Prominence & Navigation: - Moved Key Principles to top (before Quick Reference) - Added Phase column to Quick Reference in workflow order - Added 'When to use' column to Template Files - Replaced flat When NOT to Use with Framework Routing Guide (LlamaIndex vs LangGraph vs PydanticAI vs Haystack vs DSPy) Epoch 2 — Decision Guidance: - Added Where to Start table (maps existing work → pipeline entry point) - Added Pipeline Mode table (Quick/Full/Evaluate/Graph modes) Epoch 3 — Pattern Expansion: - Replaced flat gotchas with structured Troubleshooting Recovery Guide (3 symptom categories with immediate + permanent fixes) - Added references/example-rag-pipeline.md (end-to-end worked example) - Added references/evaluation-workflow.md (ParamTuner + evaluators) 17 files: SKILL.md, 11 references, 1 script, 4 templates. v1.0.0 → v1.3.0 across 3 SkillOpt epochs. Signed-off-by: Magnus Hedemark <magnus919@pm.me>
4.9 KiB
4.9 KiB
End-to-End RAG Pipeline — Worked Example
This example shows a complete LlamaIndex RAG pipeline from data loading through production evaluation. It demonstrates the expected output depth for the Full pipeline mode.
Scenario
A directory of technical PDF documentation about a REST API. We need to build a production RAG pipeline that answers developer questions with citations.
Pipeline
1. Ingest — Load Documents
from llama_index.core import SimpleDirectoryReader, Settings
from llama_index.llms.openai import OpenAI
from llama_index.embeddings.openai import OpenAIEmbedding
Settings.llm = OpenAI(model="gpt-4o-mini")
Settings.embed_model = OpenAIEmbedding(model="text-embedding-3-small")
documents = SimpleDirectoryReader("./api-docs/").load_data()
print(f"Loaded {len(documents)} documents")
# Output: Loaded 12 documents
2. Chunk — Semantic Splitting
from llama_index.core.node_parser import SemanticSplitterNodeParser
splitter = SemanticSplitterNodeParser(
embed_model=Settings.embed_model,
breakpoint_percentile_threshold=95,
buffer_size=1,
)
nodes = splitter.get_nodes_from_documents(documents)
print(f"Created {len(nodes)} chunks")
# Output: Created 347 chunks
3. Index — Vector + Hybrid
from llama_index.core import VectorStoreIndex
from llama_index.core.retrievers import BM25Retriever
# Build vector index
index = VectorStoreIndex(nodes=nodes)
# Create retrievers for hybrid search
vector_retriever = index.as_retriever(similarity_top_k=10)
bm25_retriever = BM25Retriever.from_defaults(index=index, similarity_top_k=10)
4. Retrieve — Hybrid + Rerank
from llama_index.core.retrievers import QueryFusionRetriever
from llama_index.core.postprocessor.cohere_rerank import CohereRerank
from llama_index.core.query_engine import RetrieverQueryEngine
# Fusion retriever with reciprocal rank fusion
hybrid_retriever = QueryFusionRetriever(
retrievers=[vector_retriever, bm25_retriever],
similarity_top_k=5,
num_queries=1, # Use original query only
mode="reciprocal_rerank",
)
# Reranker
reranker = CohereRerank(top_n=5)
# Query engine
query_engine = RetrieverQueryEngine.from_args(
retriever=hybrid_retriever,
node_postprocessors=[reranker],
)
5. Agent — Query with Multi-Source Routing (Optional)
from llama_index.core.agent.workflow import FunctionAgent
from llama_index.core.tools import QueryEngineTool
rag_tool = QueryEngineTool.from_defaults(
query_engine=query_engine,
name="api_docs_search",
description="Search API documentation for endpoints, parameters, and examples",
)
agent = FunctionAgent(
name="APIAgent",
system_prompt="Answer developer questions about the REST API using documentation.",
tools=[rag_tool],
)
response = await agent.run(user_msg="How do I authenticate API requests?")
# Response includes: authentication methods, required headers, token expiry
6. Evaluate — Measure Quality
from llama_index.core.evaluation import FaithfulnessEvaluator, RelevancyEvaluator
faith = FaithfulnessEvaluator()
rel = RelevancyEvaluator()
test_queries = [
"What is the rate limiting policy?",
"How do I paginate through results?",
"What error codes does the API return?",
]
for q in test_queries:
resp = query_engine.query(q)
faith_result = faith.evaluate_response(response=resp)
rel_result = rel.evaluate_response(response=resp, question=q)
print(f"Q: {q}")
print(f" Faithfulness: {faith_result.passing}")
print(f" Relevancy: {rel_result.passing}")
print(f" Sources: {len(resp.source_nodes)} chunks")
7. Deploy — Production
from llama_deploy import deploy_workflow, WorkflowServiceConfig, ControlPlaneConfig
from llama_index.core.workflow import Workflow, StartEvent, StopEvent, step
class RAGWorkflow(Workflow):
@step
async def answer(self, ev: StartEvent) -> StopEvent:
result = query_engine.query(ev.query)
return StopEvent(result=str(result))
await deploy_workflow(
workflow=RAGWorkflow(timeout=60),
workflow_config=WorkflowServiceConfig(service_name="api-rag"),
control_plane_config=ControlPlaneConfig(),
)
Expected Output Depth
- Quick mode: 2-3 source chunks, no reranker, single evaluator metric
- Full mode (shown above): 5 source chunks, hybrid retrieval + reranker, multi-metric evaluation, deploy-ready
- Evaluate mode: ParamTuner over chunk_size=[256,512,1024] with top_k=[2,5,10], reporting best configuration
Common Pitfalls in This Pipeline
- Skipping the reranker produces answers that appear correct but miss nuance
- Using SimpleVectorStore instead of a persistent vector store loses all indexed data on restart
- Forgetting
awaiton the agent call produces no error — just a silent coroutine object - The hybrid retriever returns more candidates than the synthesizer needs without the reranker