From e5e435e028239b9bcd9893f29e5ef040986f43ed Mon Sep 17 00:00:00 2001 From: Mikko Ohtamaa Date: Sat, 18 Oct 2025 17:33:04 +0300 Subject: [PATCH] Adding end to end tutorial --- README.md | 1 + docs/tutorial.md | 222 ++++++++++++++++++ .../samples/PyCon Finland 2025 Schedule.html | 71 ++++++ src/haiku/rag/client.py | 1 + src/haiku/rag/logging.py | 1 + src/haiku/rag/qa/agent.py | 13 + 6 files changed, 309 insertions(+) create mode 100644 docs/tutorial.md create mode 100644 examples/samples/PyCon Finland 2025 Schedule.html diff --git a/README.md b/README.md index 680e5dfe..8f0c2da3 100644 --- a/README.md +++ b/README.md @@ -158,6 +158,7 @@ haiku-rag a2aclient ``` The A2A agent provides: + - Multi-turn dialogue with context - Intelligent multi-search for complex questions - Source citations with titles and URIs diff --git a/docs/tutorial.md b/docs/tutorial.md new file mode 100644 index 00000000..db6ae36e --- /dev/null +++ b/docs/tutorial.md @@ -0,0 +1,222 @@ +# Tutorial + +These are the quickstart instructions to get going and familiar with `haiku.rag`. This tutorial is indented for people who are familiar with command line and Python, but not different AI ecosystem tools. + +- Install `haiku.rag` Python package +- Set up environment variables for running `haiku.rag` +- Adding and retrieving items +- Inspecting the database + +The tutorial uses OpenAI API service - no local installation needed and will work on computers with any amount of RAM and GPU. The OpenAI API is pay-as-you-go, so you need to top it up at least up top ~$5 when creating the API key. + +## Setup + +[Get an OpenAI API key](https://platform.openai.com/api-keys). + +Install `haiku.rag` Python package using [uv](https://docs.astral.sh/uv/getting-started/installation/) or your favourite Python package manager: + +```shell +# Python 3.12+ needed +uv install haiku.rag +``` + +Configure your OpenAI API key and embeddings model. + +- Haiku RAG supports [dotenv](https://pypi.org/project/python-dotenv/) environment files and environment varibles for configuration +- [See OpenAPI vector embeddings documentation](https://platform.openai.com/docs/guides/embeddings/embedding-models) +- For the list of OpenAI embedding models and `EMBEDDINGS_VECTOR_DIM`, ask ChatGPT for instructions + +Create a file called `.env` and add: + +```shell +# +# These settings are relevant for converting documents to embeddings +# + +EMBEDDINGS_PROVIDER="openai" +# or text-embedding-3-large +EMBEDDINGS_MODEL="text-embedding-3-small" +EMBEDDINGS_VECTOR_DIM=1536 +OPENAI_API_KEY="" + +# +# These settings are relevant for question answering chats +# + +# We tell Haiku.rag to use OpenAI remote AI for chats, instead of local ollama. +QA_PROVIDER="openai" +QA_MODEL="gpt-4o-mini" # or gpt-4, gpt-3.5-turbo, etc. +``` + +## Adding the first documents + +Now you can add some pieces of text in the database: + +```shell +haiku-rag add "Python is the best programming language in the world, because it is flexible, with robust ecosystem, open source licensing and thousands of contributors" +haiku-rag add "JavaScript is a popular programming language, but has a lot of warts" +haiku-rag add "PHP is a bad programming language, because of spotted security history, horrible syntax and declining popularity" +``` + +What will happen + +- The piece of text is send to OpenAI `/embeddings` API service +- OpenAI translates the free form text to RAG embedding vectors needed for the retrieval +- The vector values will be stored in a local database + +Show the database: + +```shell +haiku-rag info +``` + +You should get the back the [LanceDB](https://lancedb.com/) database information: + +``` +haiku.rag database info + path: /Users/moo/Library/Application Support/haiku.rag/haiku.rag.lancedb + haiku.rag version (db): 0.12.1 + embeddings: openai/text-embedding-3-small (dim: 1536) + documents: 4 + versions (documents): 9 + versions (chunks): 10 +────────────────────────────────────────────────────────────────────────────────── +Versions + haiku.rag: 0.12.1 + lancedb: 0.25.2 + docling: 2.57.0 +``` + +## Asking questions and retrieving information + +Now we can use OpenAI to retrieve information from our embeddings database. + +In this example, we connect to a remote OpenAI API instead of local ollama. +Mak + +Behind the scenes [pydantic-ai](https://ai.pydantic.dev/) query is created +using `OpenAIChatModel.request()`. + +```shell +haiku-rag ask "What is the best programming language in the world" +``` + +``` +Question: What is the best programming language in the world + +Answer: +According to the document, Python is considered the best programming language in the world due to its flexibility, robust ecosystem, open-source licensing, and thousands of contributors. +``` + +## Python information retrieval + +You can interact with Haiku RAG from Python in a similar manner as you can from the command line. Here we use Haiku RAG with the interactive Python command prompt (REPL). + +First we need to install `ipython` as the normal Python REPL does not work + +```shell +uv pip install ipython +``` + +Run IPython: + +```shell +ipython +``` + +Then copy paste in the snippet (you can use [%cpaste](https://ipythonbook.com/magic/cpaste.html) command): + +```python +import sys +import logging +from haiku.rag.client import HaikuRAG + +# Increase logging verbosity so we see what happens behind the scenes, +# and check that the logger works +logging.basicConfig( + stream=sys.stdout, + level=logging.DEBUG, + format="%(name)s - %(levelname)s - %(message)s", +) +logger = logging.getLogger() +logger.setLevel(logging.DEBUG) +logger.debug("AGI here we come") + +# Uses LanceDB database from Config.DEFAULT_DATA_DIR +async with HaikuRAG() as client: + answer = await client.ask("What is the best programming language in the world?") + print(answer) + +``` + +You should see: + +``` +2025-10-18 17:05:49,611 - DEBUG - HTTP Response: POST https://api.openai.com/v1/chat/completions "200 OK" Headers({'date': 'Sat, 18 Oct 2025 14:05:49 GMT', 'content-type': 'application/json', 'transfer-encoding': 'chunked', 'connection': 'keep-alive', 'access-control-expose-headers': 'X-Request-ID', 'openai-organization': 'xxx', 'openai-processing-ms': '788', 'openai-project': 'xxx', 'openai-version': '2020-10-01', 'x-envoy-upstream-service-time': '1050', 'x-ratelimit-limit-requests': '10000', 'x-ratelimit-limit-tokens': '200000', 'x-ratelimit-remaining-requests': '9998', 'x-ratelimit-remaining-tokens': '199603', 'x-ratelimit-reset-requests': '14.981s', 'x-ratelimit-reset-tokens': '119ms', 'x-request-id': 'req_9651a3691a144dd388e97066ad67a49c', 'x-openai-proxy-wasm': 'v0.1', 'cf-cache-status': 'DYNAMIC', 'strict-transport-security': 'max-age=31536000; includeSubDomains; preload', 'x-content-type-options': 'nosniff', 'server': 'cloudflare', 'cf-ray': '990897b6f8d270d7-ARN', 'content-encoding': 'gzip', 'alt-svc': 'h3=":443"; ma=86400'}) +2025-10-18 17:05:49,611 - DEBUG - request_id: req_9651a3691a144dd388e97066ad67a49c + +According to the document, Python is considered the best programming language in the world due to its flexibility, robust ecosystem, open-source licensing, and support from thousands of contributors. +``` + +## Complex documents + +Haiku RAG can also handle types beyond plain text, assuming your AI backend knowns about this. + +Here we add research papers about Python from [arxiv](https://arxiv.org/search/?query=python&searchtype=all&source=header). + +````shell +# Better Python Programming for all: With the focus on Maintainability +haiku-rag add-src --meta collection="Interesting Python papers" "https://arxiv.org/pdf/2408.09134" +# Interoperability From OpenTelemetry to Kieker: Demonstrated as Export from the Astronomy Shop +haiku-rag add-src --meta collection="Interesting Python papers" "https://arxiv.org/pdf/2510.11179" +``` + +Then we can query this: + +```shell +haiku-rag ask "Who wrote a paper about OpenTelemetry interoperability, and what was his take" +``` + +We should get something along the lines: + +``` +Answer: +David Georg Reichelt from Lancaster University wrote a paper titled "Interoperability From OpenTelemetry to Kieker: Demonstrated as Export from the Astronomy Shop." In his work, he indicates that there is a structural difference between Kieker’s synchronous traces and OpenTelemetry’s asynchronous traces, leading to limited compatibility between the two systems. This highlights the challenges of interoperability in observability frameworks. +``` + +We can also do offline PDF (to ensure OpenAI does not cheat) - a file we know that should not very well known in Internet: + +```shell +# This static file is supplied with haiku.rag repo +haiku-rag add-src "examples/samples/PyCon Finland 2025 Schedule.html" +``` + +And then: + +```shell +haiku-rag ask "Who were presenting talks in Pycon Finland 2025? Can you give at least five different people." +``` + +``` +The following people are presenting talks at PyCon Finland 2025: + + 1 Jeremy Mayeres - Talk: The Limits of Imagination: An Open Source Journey + 2 Aroma Rodrigues - Talk: Python and Rust, a Perfect Pairing + 3 Andreas Jung - Talk: Guillotina Volto: A New Backend for Volto + 4 Daniel Vahla - Talk: Experiences with AI in Software Projects + 5 Andreas Jung (also presenting another talk) - Talk: Debugging Python + ``` + +## Reseting the embeddings database + +If you change your embeddings provider (OpenAI -> ollama) or its parameters, you need to delete the LanceDB database and add the documents again: + +```shell +rm -rf "/Users/moo/Library/Application Support/haiku.rag/haiku.rag.lancedb" +```` + +## Configuration + +See [Configuration page](./configuration.md) for more information about configurait + +For the available environment variable config options see [config.py](https://github.com/ggozad/haiku.rag/blob/main/src/haiku/rag/config.py). diff --git a/examples/samples/PyCon Finland 2025 Schedule.html b/examples/samples/PyCon Finland 2025 Schedule.html new file mode 100644 index 00000000..660dea39 --- /dev/null +++ b/examples/samples/PyCon Finland 2025 Schedule.html @@ -0,0 +1,71 @@ + + +PyCon Finland 2025 Schedule

PyCon Finland 2025 Schedule

Friday October 17, 2025, as part of Plone Conference 2025 in Jyväskylä, Finland.

Notice! Changes to the schedule are still possible! Get your tickets, only 100€!

Notice: The times in the calendar will be shown on your local time. The actual event time is UTC +3:00 (Helsinki).

PS. Check out all Plone Conference talks and calendar. Read more about PyCon Finland 2025.

Auditorio 1
Alfa
Beeta
09:00
Registration opens at the Lobby
09:15
Other
Case Study
Auditorio 1
en
This + talk covers a lot of ground. I'm moving between three levels of +abstraction: a concrete case story, software patterns, and ultimately a +living systems paradigm.
Case Study
Python
Beeta
en
eduTAP + is a project to bring campus cards into the smart phone wallets. +eduTAP is implemented in Python, by members of the Plone Community.
Case Study
Alfa
en
Design for your constraints, communicate your assumptions, and sometimes boring wins over brilliant.
10:00
AI
Auditorio 1
en
“A + fool with a tool is still a fool” examines how AI is reshaping software + development—what it truly does well, where it fails spectacularly, and +how to use it responsibly rather than recklessly. Drawing on real-world +wins and faceplants (including “vibe coding”), the talk distills +practical guardrails, governance, and senior-level responsibilities for +shipping trustworthy systems.
React
Plone: Backend
Python
Beeta
en
In + this talk, we’ll introduce Guillotina Volto, an experimental +integration that uses Guillotina — an async Python REST API framework — +as a new backend for Volto.
Python
Alfa
en
A + recap on thread safety and synchronisation primitives. As more +developers adopt free-threaded Python, understanding thread safety +becomes critical - not just nice to have. This talk explores all +essential synchronisation primitives from Python's threading module +through a practical dice game simulation scenario. You'll learn to +identify and fix race conditions, understand what the GIL was +protecting, and discover patterns to ensure your code works correctly in + the free-threaded future.
10:00
AI
Auditorio 1
en
In my talk, I’ll share practical AI experiences from projects over the past two years.
Python
Beeta
en
Debugging + can be a stressful experience to a developer for many reasons. In this +talk, I provide practical and hands-on tips that you can bring to your +work or hobby projects the following day to turn your debugging session +from stressful encounters to joyful experiences.
10:30
Community
Auditorio 1
en
Plone Foundation Annual General Meeting. Vote for a new board and other topics.
11:00
11:30
Keynote
Auditorio 1
en
Muuttolintujen + Kevät (“Spring of migratory birds”) is a mobile application –based +citizen science campaign where citizens collect bird observations with +help of an automated bird sound classifier of Finnish birds.
12:30
Python
Alfa
en
This talk presents three real-world scenarios where Python acts as the glue between specialized languages.
13:30
AI
Auditorio 1
en
Building + AI applications on your own data offers many opportunities; +Retrieval-Augmented Generation (RAG) can make LLMs more accurate and +relevant. But where should you store your data and vectors?
AI
Python
Beeta
en
The OG cast of developers who brought you Zope, CMF, Plone and Pyramid join up to make AI fun.
Python
Alfa
en
Shiny + for Python allows you to build interactive web applications using only +Python code. Inspired by the original Shiny framework for R, it +introduces a reactive programming model that makes it easy to link UI +elements to Python functions.
14:15
Python
Auditorio 1
en
In + this talk I will cover some of the recommendations I make as a staff +engineer on how to deliver quality in such a field, and what you can do +to level up.
Python
Beeta
en
Let me take you on a journey—a journey of bold ideas, spectacular failures, and the rare flash of brilliance.
14:45
React
Python
Alfa
en
In this talk, we take a look at why and how to do it with tools like rustimport and Maturin.
15:00
Python
Auditorio 1
en
Typing + in Python starts to take shape via many PEPS and we are going to +explore the evolution of it. In this talk, we will look at how do we get + here, with all the tools that can be used to enforce strong typing in +Python, a genetically dynamic typing language.
Plone: Frontend
Webdesign
Beeta
en
In + this talk, we’ll explore how to make accessibility-first development +part of your organization’s DNA. You’ll learn how assistive technologies + interact with the accessibility tree, what accessibility-first +development really means, why accessible design benefits all users—not +just people with disabilities—and we will break some common biases that +still exist around accessibility.
Plone: DevOps
Beeta
en
This + talk will share why we made the switch, how we approached the +transition, and what tools (like Kubernetes runners and composite +actions) helped us succeed.
15:30
Other
Open Source
Auditorio 1
en
An + attempt to explain why feminism can be an interesting approach to +questioning our behaviors and practices as open source contributors, as +members of the software industry, and as developers.
Process
Alfa
en
This + is story of what mistakes I have done in my projects, why I now +understand my developers colleagues better and what I have done to +regain my past knowledge as a tester.
React
Plone: Frontend
Plone: Backend
Beeta
en
This + talk shares how I brought back a key part of Plone, the workflow +manager, by rebuilding it for Volto and the new REST based backend.
Python
Alfa
en
If + you care about Python at scale—web servers, data pipelines, or +reinforcement-learning loops—this session is your roadmap to a GIL-free +future.
16:00
AI
Python
Auditorio 1
en
DSPy + is a declarative framework for building modular, self-improving AI +systems using structured code and natural-language modules, enabling +fast iteration, composability, and model-agnostic deployment.
16:45
17:00
Community
Auditorio 1
en
Sprints Info. Information about the Plone development sprint on Saturday and Sunday. Where, what, how, topics.
17:15
Auditorio 1
Lightning talks consists of 5 min slots where people can quickly talk about something important and interesting.
18:00
Auditorio 1
en
Plone Conference 2025 and PyCon Finland 2025 has ended. See you all at the next time!

Location is Agora building at University of Jyväskylä.

+ + \ No newline at end of file diff --git a/src/haiku/rag/client.py b/src/haiku/rag/client.py index 686d7414..47b6408a 100644 --- a/src/haiku/rag/client.py +++ b/src/haiku/rag/client.py @@ -582,6 +582,7 @@ class HaikuRAG: from haiku.rag.qa import get_qa_agent qa_agent = get_qa_agent(self, use_citations=cite, system_prompt=system_prompt) + logger.debug("Using QA agent: %s", qa_agent.__class__.__name__) return await qa_agent.answer(question) async def rebuild_database(self) -> AsyncGenerator[str, None]: diff --git a/src/haiku/rag/logging.py b/src/haiku/rag/logging.py index d727f84e..24bb90fc 100644 --- a/src/haiku/rag/logging.py +++ b/src/haiku/rag/logging.py @@ -7,6 +7,7 @@ from rich.logging import RichHandler def get_logger() -> logging.Logger: """Return the library logger configured with a Rich handler.""" + logger = logging.getLogger("haiku.rag") handler = RichHandler( diff --git a/src/haiku/rag/qa/agent.py b/src/haiku/rag/qa/agent.py index f1abe7ab..9c157e7c 100644 --- a/src/haiku/rag/qa/agent.py +++ b/src/haiku/rag/qa/agent.py @@ -1,3 +1,5 @@ +import logging + from pydantic import BaseModel, Field from pydantic_ai import Agent, RunContext from pydantic_ai.models.openai import OpenAIChatModel @@ -9,6 +11,9 @@ from haiku.rag.config import Config from haiku.rag.qa.prompts import QA_SYSTEM_PROMPT, QA_SYSTEM_PROMPT_WITH_CITATIONS +logger = logging.getLogger(__name__) + + class SearchResult(BaseModel): content: str = Field(description="The document text content") score: float = Field(description="Relevance score (higher is more relevant)") @@ -47,6 +52,14 @@ class QuestionAnswerAgent: retries=3, ) + logger.info( + "Initialized QuestionAnswerAgent %s, with agent: %s, model: %s, client: %s", + self, + self._agent, + model_obj, + client, + ) + @self._agent.tool async def search_documents( ctx: RunContext[Dependencies],