Simplify agentic vectorless RAG demo (#191)

* Simplify and fix agentic RAG demo

* Show labeled reasoning output in RAG demo

* Comment out reasoning model settings by default
This commit is contained in:
Ray 2026-03-28 09:42:46 +08:00 committed by GitHub
parent 4002dc94de
commit d50c293309
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -14,7 +14,6 @@ Steps:
1 Index PDF and inspect tree structure 1 Index PDF and inspect tree structure
2 Inspect document metadata 2 Inspect document metadata
3 Ask a question (agent auto-calls tools) 3 Ask a question (agent auto-calls tools)
4 Reload from workspace and verify persistence
""" """
import os import os
import sys import sys
@ -25,15 +24,17 @@ import requests
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from agents import Agent, ItemHelpers, Runner, function_tool from agents import Agent, Runner, function_tool
from agents.model_settings import ModelSettings
from agents.stream_events import RawResponsesStreamEvent, RunItemStreamEvent from agents.stream_events import RawResponsesStreamEvent, RunItemStreamEvent
from openai.types.responses import ResponseTextDeltaEvent, ResponseReasoningSummaryTextDeltaEvent # noqa: F401 from openai.types.responses import ResponseTextDeltaEvent, ResponseReasoningSummaryTextDeltaEvent
from pageindex import PageIndexClient from pageindex import PageIndexClient
import pageindex.utils as utils import pageindex.utils as utils
_EXAMPLES_DIR = os.path.dirname(os.path.abspath(__file__))
PDF_URL = "https://arxiv.org/pdf/2603.15031" PDF_URL = "https://arxiv.org/pdf/2603.15031"
_EXAMPLES_DIR = os.path.dirname(os.path.abspath(__file__))
PDF_PATH = os.path.join(_EXAMPLES_DIR, "documents", "attention-residuals.pdf") PDF_PATH = os.path.join(_EXAMPLES_DIR, "documents", "attention-residuals.pdf")
WORKSPACE = os.path.join(_EXAMPLES_DIR, "workspace") WORKSPACE = os.path.join(_EXAMPLES_DIR, "workspace")
@ -48,12 +49,7 @@ ANSWERING: Answer based only on tool output. Be concise.
""" """
def query_agent( def query_agent(client: PageIndexClient, doc_id: str, prompt: str, verbose: bool = False) -> str:
client: PageIndexClient,
doc_id: str,
prompt: str,
verbose: bool = False,
) -> str:
"""Run a document QA agent using the OpenAI Agents SDK. """Run a document QA agent using the OpenAI Agents SDK.
Streams text output token-by-token and returns the full answer string. Streams text output token-by-token and returns the full answer string.
@ -84,42 +80,50 @@ def query_agent(
instructions=AGENT_SYSTEM_PROMPT, instructions=AGENT_SYSTEM_PROMPT,
tools=[get_document, get_document_structure, get_page_content], tools=[get_document, get_document_structure, get_page_content],
model=client.retrieve_model, model=client.retrieve_model,
# model_settings=ModelSettings(reasoning={"effort": "low", "summary": "auto"}), # Uncomment to enable reasoning
) )
async def _run(): async def _run():
collected = []
streamed_this_turn = False
streamed_run = Runner.run_streamed(agent, prompt) streamed_run = Runner.run_streamed(agent, prompt)
current_stream_kind = None
async for event in streamed_run.stream_events(): async for event in streamed_run.stream_events():
if isinstance(event, RawResponsesStreamEvent): if isinstance(event, RawResponsesStreamEvent):
if isinstance(event.data, ResponseReasoningSummaryTextDeltaEvent): if isinstance(event.data, ResponseReasoningSummaryTextDeltaEvent):
print(event.data.delta, end="", flush=True) if current_stream_kind != "reasoning":
elif isinstance(event.data, ResponseTextDeltaEvent): if current_stream_kind is not None:
print()
print("\n[reasoning]: ", end="", flush=True)
delta = event.data.delta delta = event.data.delta
print(delta, end="", flush=True) print(delta, end="", flush=True)
collected.append(delta) current_stream_kind = "reasoning"
streamed_this_turn = True elif isinstance(event.data, ResponseTextDeltaEvent):
if current_stream_kind != "text":
if current_stream_kind is not None:
print()
print("\n[text]: ", end="", flush=True)
delta = event.data.delta
print(delta, end="", flush=True)
current_stream_kind = "text"
elif isinstance(event, RunItemStreamEvent): elif isinstance(event, RunItemStreamEvent):
item = event.item item = event.item
if item.type == "message_output_item": if item.type == "tool_call_item":
if not streamed_this_turn: if current_stream_kind is not None:
text = ItemHelpers.text_message_output(item) print()
if text:
print(f"{text}")
streamed_this_turn = False
collected.clear()
elif item.type == "tool_call_item":
if streamed_this_turn:
print() # end streaming line before tool call
raw = item.raw_item raw = item.raw_item
args = getattr(raw, "arguments", "{}") args = getattr(raw, "arguments", "{}")
args_str = f"({args})" if verbose else "" args_str = f"({args})" if verbose else ""
print(f"[tool call]: {raw.name}{args_str}") print(f"\n[tool call]: {raw.name}{args_str}", flush=True)
current_stream_kind = None
elif item.type == "tool_call_output_item" and verbose: elif item.type == "tool_call_output_item" and verbose:
if current_stream_kind is not None:
print()
output = str(item.output) output = str(item.output)
preview = output[:200] + "..." if len(output) > 200 else output preview = output[:200] + "..." if len(output) > 200 else output
print(f"[tool output]: {preview}\n") print(f"\n[tool call output]: {preview}", flush=True)
return "".join(collected) current_stream_kind = None
if current_stream_kind is not None:
print()
return "" if not streamed_run.final_output else str(streamed_run.final_output)
try: try:
asyncio.get_running_loop() asyncio.get_running_loop()
@ -129,7 +133,9 @@ def query_agent(
return asyncio.run(_run()) return asyncio.run(_run())
# ── Download PDF if needed ───────────────────────────────────────────────────── if __name__ == "__main__":
# Download PDF if needed
if not os.path.exists(PDF_PATH): if not os.path.exists(PDF_PATH):
print(f"Downloading {PDF_URL} ...") print(f"Downloading {PDF_URL} ...")
os.makedirs(os.path.dirname(PDF_PATH), exist_ok=True) os.makedirs(os.path.dirname(PDF_PATH), exist_ok=True)
@ -141,15 +147,17 @@ if not os.path.exists(PDF_PATH):
f.write(chunk) f.write(chunk)
print("Download complete.\n") print("Download complete.\n")
# ── Setup ────────────────────────────────────────────────────────────────────── # Setup
client = PageIndexClient(workspace=WORKSPACE) client = PageIndexClient(workspace=WORKSPACE)
# ── Step 1: Index + Tree ─────────────────────────────────────────────────────── # Step 1: Index + Tree
print("=" * 60) print("=" * 60)
print("Step 1: Indexing PDF and inspecting tree structure") print("Step 1: Indexing PDF and inspecting tree structure")
print("=" * 60) print("=" * 60)
doc_id = next((did for did, doc in client.documents.items() doc_id = next(
if doc.get('doc_name') == os.path.basename(PDF_PATH)), None) (did for did, doc in client.documents.items() if doc.get('doc_name') == os.path.basename(PDF_PATH)),
None,
)
if doc_id: if doc_id:
print(f"\nLoaded cached doc_id: {doc_id}") print(f"\nLoaded cached doc_id: {doc_id}")
else: else:
@ -159,16 +167,17 @@ print("\nTree Structure (top-level sections):")
structure = json.loads(client.get_document_structure(doc_id)) structure = json.loads(client.get_document_structure(doc_id))
utils.print_tree(structure) utils.print_tree(structure)
# ── Step 2: Document Metadata ────────────────────────────────────────────────── # Step 2: Document Metadata
print("\n" + "=" * 60) print("\n" + "=" * 60)
print("Step 2: Document Metadata (get_document)") print("Step 2: Document Metadata (get_document)")
print("=" * 60) print("=" * 60)
print(client.get_document(doc_id)) doc_metadata = client.get_document(doc_id)
print(f"\n{doc_metadata}")
# ── Step 3: Agent Query ──────────────────────────────────────────────────────── # Step 3: Agent Query
print("\n" + "=" * 60) print("\n" + "=" * 60)
print("Step 3: Agent Query (auto tool-use)") print("Step 3: Agent Query (auto tool-use)")
print("=" * 60) print("=" * 60)
question = "Explain Attention Residuals in simple language." question = "Explain Attention Residuals in simple language."
print(f"\nQuestion: '{question}'\n") print(f"\nQuestion: '{question}'")
query_agent(client, doc_id, question, verbose=True) query_agent(client, doc_id, question, verbose=True)