Files
wehub-resource-sync c3bf08ac8d
K8s Workspace Integration Tests / k8s-workspace-tests (push) Has been cancelled
Pre-commit / run (ubuntu-latest) (push) Has been cancelled
Python Unittest Coverage / test (macos-15, 3.11) (push) Has been cancelled
Python Unittest Coverage / test (ubuntu-latest, 3.11) (push) Has been cancelled
Python Unittest Coverage / test (windows-latest, 3.11) (push) Has been cancelled
Web UI / check (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:39:27 +08:00

199 lines
6.9 KiB
Python

# -*- coding: utf-8 -*-
"""Wire :class:`RAGMiddleware` into an :class:`Agent` — library mode.
The middleware in :mod:`agentscope.middleware._rag` is the agent-side
half of RAG: given one or more :class:`~agentscope.rag.KnowledgeBase`
handles (each pairing an embedding model with a vector-store
collection), the middleware drives search on each new user turn and
feeds the matched chunks into the model context.
This example reuses the indexing pipeline shown in ``index_and_search.py``
(parse → chunk → embed → insert) and then attaches the same knowledge base
to two agents — one per mode:
- ``"static"``: on the first reasoning step of a reply, embed the
user's question, search, and inject the top hits as a one-shot
:class:`HintBlock` into the agent's context. The model sees the
matched snippets but never "decides" to search.
- ``"agentic"`` (the default): expose a ``search_knowledge`` tool. The
model decides when (and what) to search, the same way it decides any
other tool call.
Run with::
DASHSCOPE_API_KEY=sk-... python examples/rag/integrate_with_agent.py
"""
import asyncio
import os
from agentscope.agent import Agent
from agentscope.credential import DashScopeCredential
from agentscope.embedding import DashScopeEmbeddingModel
from agentscope.message import UserMsg
from agentscope.middleware import RAGMiddleware
from agentscope.model import DashScopeChatModel
from agentscope.rag import (
ApproxTokenChunker,
KnowledgeBase,
QdrantStore,
TextParser,
)
from agentscope.tool import Toolkit
COLLECTION = "demo-kb"
KNOWLEDGE: dict[str, bytes] = {
"company-policy.md": (
b"# Acme Remote Work Policy\n\n"
b"Employees may work remotely up to three days per week. "
b"Wednesdays are mandatory in-office days for the whole "
b"engineering org so cross-team syncs land on a predictable "
b"day.\n\n"
b"Equipment stipend: each new hire receives a USD 1,500 "
b"one-off stipend for a home-office setup. Receipts must be "
b"submitted within 90 days of the start date.\n"
),
"release-notes.md": (
b"# AgentScope 3.0 release notes\n\n"
b"- New ``agentscope.rag`` module: pluggable parser, chunker, "
b"embedding, and vector-store backends.\n"
b"- ``RAGMiddleware`` ships in two modes -- ``static`` for "
b"automatic injection, ``agentic`` for tool-driven search.\n"
b"- Knowledge base service supports embedded and dedicated "
b"worker deployments through a single message-bus channel.\n"
),
}
async def index_corpus(knowledge: KnowledgeBase) -> None:
"""Index the demo corpus into the knowledge base.
Identical pipeline to ``examples/rag/index_and_search.py`` — extracted
as a helper here so the agent-side wiring stays the focus. Each source
file becomes one logical document; ``KnowledgeBase.insert_document``
embeds and inserts every chunk in a single batch.
"""
parser = TextParser()
chunker = ApproxTokenChunker(chunk_size=256, overlap=32)
for filename, file_bytes in KNOWLEDGE.items():
sections = await parser.parse(file=file_bytes, filename=filename)
chunks = await chunker.chunk(sections)
await knowledge.insert_document(
chunks,
document_metadata={"filename": filename},
)
def build_agent(
name: str,
*,
chat_model: DashScopeChatModel,
rag_mw: RAGMiddleware,
) -> Agent:
"""Construct an :class:`Agent` with the RAG middleware attached.
The middleware is just one entry in the ``middlewares=`` list; it
composes with every other middleware (tool offload, mem0, ...) the
agent uses.
"""
return Agent(
name=name,
system_prompt=(
"You are a concise assistant. Use matched context when "
"available; if you don't know, say so."
),
model=chat_model,
toolkit=Toolkit(),
middlewares=[rag_mw],
)
async def ask(agent: Agent, question: str) -> None:
"""Run one reply and print the final assistant message."""
print(f"\n[{agent.name}] user: {question}")
reply = await agent.reply(UserMsg(name="user", content=question))
print(f"[{agent.name}] assistant: {reply.get_text_content()}")
async def main() -> None:
"""The main entry point of the example."""
api_key = os.environ.get("DASHSCOPE_API_KEY")
if not api_key:
raise RuntimeError(
"Set DASHSCOPE_API_KEY before running this example.",
)
credential = DashScopeCredential(api_key=api_key)
chat_model = DashScopeChatModel(
credential=credential,
model="qwen-plus",
stream=False,
)
embedding_model = DashScopeEmbeddingModel(
credential=credential,
model="text-embedding-v4",
dimensions=1024,
)
store = QdrantStore(location=":memory:")
async with store:
# One :class:`KnowledgeBase` handle binds embedding + vector store +
# collection together. ``insert_document`` / ``search`` /
# ``list_documents`` all go through it, and the backing
# collection is created lazily on first use.
knowledge = KnowledgeBase(
name="acme-handbook",
description="Acme HR policies and AgentScope 3.0 release notes.",
embedding_model=embedding_model,
vector_store=store,
collection=COLLECTION,
)
await index_corpus(knowledge)
# ---- Mode 1: static ----
# Search is automatic on the first reasoning step. The injected
# ``HintBlock`` is one-shot (removed after the model call) so it
# doesn't poison the next turn.
static_mw = RAGMiddleware(
knowledge_bases=[knowledge],
parameters=RAGMiddleware.Parameters(
mode="static",
top_k=3,
emit_hint_event=False,
),
)
static_agent = build_agent(
"rag-static-agent",
chat_model=chat_model,
rag_mw=static_mw,
)
await ask(
static_agent,
"How many remote days per week does Acme allow?",
)
# ---- Mode 2: agentic ----
# The middleware exposes a ``search_knowledge`` tool instead of
# auto-injecting. The model decides when to call it; it may
# also pass ``knowledge_bases=[...]`` to scope the search when
# multiple knowledge bases are bound.
agentic_mw = RAGMiddleware(
knowledge_bases=[knowledge],
parameters=RAGMiddleware.Parameters(mode="agentic", top_k=3),
)
agentic_agent = build_agent(
"rag-agentic-agent",
chat_model=chat_model,
rag_mw=agentic_mw,
)
await ask(
agentic_agent,
"Summarise what's new in the AgentScope 3.0 release notes.",
)
if __name__ == "__main__":
asyncio.run(main())