动手做过Agent项目的朋友,多少都体会过那种无力感——明明想让Agent往东,它偏往西。特别是在多智能体系统(MAS)上,投入大量数据微调,以为能换来稳定的答案,结果还是一言难尽。所谓100%准确率的RAG,从数据入库到用户端查询,中间每一步都充满选择,稍有不慎就偏离目标。
说实话,高分RAG的底层逻辑,我们之前已经拆开讲透——用知识图谱增强RAG,让Agent能根据任务自主探索知识图谱或任意数据库,找到支撑解决问题的直接数据。不存在模糊相似,更不存在胡编乱造。比如问“如何制备大尺寸WSe2,实验室应注意哪些调整?”Agent会从笔记中定位到冷壁CVD、卤化物辅助生长、MOCVD、扩散控制外延等方法,并提炼出温度控制、卤化物选择、基底处理等关键调整点。

今天这个内容,是“灵丹”系列的加餐。如果你还不了解,可以先看看这边:“灵丹”——知识图谱+RAG项目官方wiki。由于上下文长度和幻觉的限制,我们不能一次给大模型太多任务。现在的策略是:把生产系统下的任务拆成清晰明确的小任务,以工作流的方式让Agent完成,把它框在一个象限里干活,杜绝胡思乱想。小任务干完,大任务自然就完成了。
典型场景包括:长文本标书撰写(每个智能体负责一部分,最后合成高质量标书)、医疗领域MAS(每个节点是一个tool,上游Agent合成健康管理)、电脑配置清单推荐(智能体共享记忆解决兼容性问题)——N种复杂任务都适用,价值非常显著。
这个系列本身是LangGraph动手实践的素材,放在前面让大家先了解价值,学基础时更有目标感。独立出来之后,我们会覆盖以下内容:① Agent+GraphRAG的100%准确率如何实现?有什么价值?② 在Win11系统搭建实践环境;③ 把PDF做成Agent可探索的知识图谱范式(元素+连接文本块);④ 接入KG做Agent的工作流,让智能体顺畅探索数据。整个内容以实操为主,每个人都能拿到结果,再返回到基础理论,才算融会贯通。
第一部分:100%准确率如何实现?有什么价值?
一个高分RAG,数据入库和查询策略各占一半比重。相似不等于相关,而使用知识图谱作为数据库,找到的就是“相关”。
1.1 数据入库,50%
在知识图谱中,不存在的客观事实不会凭空产生节点。除了提取节点,我们还会把数据源中与节点相关的信息一并抽取出来。相比传统的文本块向量嵌入,这种做法有几个优势:
- 摒弃与节点无关的信息,降低模型处理时的幻觉风险。传统文本分块中有大量无关数据,会严重干扰大模型总结。
- 让有效信息填入大模型的上下文长度。智能体找到的就是根数据,除非top_k=1,否则传统文本块大概率超出长度被截断,大模型处理效果自然差。
更多优势和劣势,在“灵丹”系列已经详细聊过,可以查看RAG-灵丹上线!
1.2 查询策略,那另一半
除了数据入库,在查询策略上我们为每个查询都设计了多步查询。以前做RAG时用过查询扩写、多步查询等策略(参考手把手做高阶RAG!最牛五大知识检索!RAG性能直线提升!44/45),现在可以根据任务给智能体安排一种或多种查询策略,确保能解决问题。有了这个行动/查询计划,智能体一步步去完成——注意,是一步步地完成,绕开上下文限制,把大象一口口吃下去。每一步都有一个独立的Agent来支撑(1个Agent = 1个大模型)。
第二部分:Win11系统的环境搭建
话不多说,直接在Windows系统搭建整个实践环境。开始前需要你本地有AI基础环境,如果还没有,可以参考之前的教程(第四天!0基础微调大模型+知识库,部署在微信!手把手安装AI必备环境!4/45)。
2.1 安装Neo4j
不同系统安装方法不同,可以在Neo4j官网找到自己系统的搭建方式。在Windows 11下,我们将Neo4j主程序放到一个无中文路径的目录(例如E:\neo4j000),记住这个路径。按Win+R呼出运行,输入sysdm.cpl并确认。之后打开Windows PowerShell,输入neo4j console启动。启动后打开7474端口进入管理界面,选择bolt模式,设置账号密码。一定要记住账密,后面需要用它连接知识图谱。
2.2 搭建实践环境
使用conda管理环境(1个课程=1个环境,避免依赖冲突)。创建环境:
conda create -n agent python=3.10
激活环境:
conda activate agent
安装依赖(先cd进入存放文件的目录):
pip install -r requirements.txt
进入jupyter实操环境:
jupyter lab
它会自动跳转到浏览器,两节课的代码都在里面了。先打开第一P的内容,说明环境已经搭建成功。
第三部分:知识图谱关键节点+事实建模
现在要把PDF文档做成知识图谱。这里使用会员提供的数据(约8000多个节点),你也可以换成任何PDF数据,放到文件夹下。代码会加载目录下所有PDF文件。接下来一步步跑代码,下面展开核心代码部分。
连接知识图谱(默认端口7687,账密为刚才设置的):
os.environ["NEO4J_URI"] = "bolt://localhost:7687"
os.environ["NEO4J_USERNAME"] = "neo4j"
os.environ["NEO4J_PASSWORD"] = "password"
graph = Neo4jGraph(refresh_schema=False)
graph.query("CREATE CONSTRAINT IF NOT EXISTS FOR (c:Chunk) REQUIRE c.id IS UNIQUE")
graph.query("CREATE CONSTRAINT IF NOT EXISTS FOR (c:AtomicFact) REQUIRE c.id IS UNIQUE")
graph.query("CREATE CONSTRAINT IF NOT EXISTS FOR (c:KeyElement) REQUIRE c.id IS UNIQUE")
graph.query("CREATE CONSTRAINT IF NOT EXISTS FOR (d:Document) REQUIRE d.id IS UNIQUE")
加载本地PDF,将文件路径改为你自己的:
loader = PyPDFDirectoryLoader(
path="E:\neo4j000\data",
glob="**/[!.]*.pdf",
silent_errors=False,
load_hidden=False,
recursive=False,
extract_images=False,
password=None,
mode="page",
headers=None,
extraction_mode="plain",
)
documents = loader.load()
for doc in documents:
print(doc)
写提取提示词。根据数据语言调整,这里会员源数据为英文,所以写英文提示词(中文数据则写中文提示词):
construction_system = """
You are now an intelligent assistant tasked with meticulously extracting both key elements and
atomic facts from a long text.
1. Key Elements: The essential nouns (e.g., characters, times, events, places, numbers), verbs (e.g.,
actions), and adjectives (e.g., states, feelings) that are pivotal to the text’s narrative.
2. Atomic Facts: The smallest, indivisible facts, presented as concise sentences. These include
propositions, theories, existences, concepts, and implicit elements like logic, causality, event
sequences, interpersonal relationships, timelines, etc.
Requirements:
#####
1. Ensure that all identified key elements are reflected within the corresponding atomic facts.
2. You should extract key elements and atomic facts comprehensively, especially those that are
important and potentially query-worthy and do not lea ve out details.
3. Whenever applicable, replace pronouns with their specific noun counterparts (e.g., change I, He,
She to actual names).
4. Ensure that the key elements and atomic facts you extract are presented in the same language as
the original text (e.g., English or Chinese).
"""
construction_human = """Use the given format to extract information from the
following input: {input}"""
construction_prompt = ChatPromptTemplate.from_messages([
("system", construction_system),
("human", ("Use the given format to extract information from the following input: {input}"))
])
对文本块进行分块(2000 token一块),提取关键节点和事实,然后导入并创建关系与链接:
async def process_document(text, document_name, chunk_size=2000, chunk_overlap=200):
start = datetime.now()
print(f"Started extraction at: {start}")
text_splitter = TokenTextSplitter(chunk_size=chunk_size, chunk_overlap=chunk_overlap)
texts = text_splitter.split_text(text)
print(f"Total text chunks: {len(texts)}")
tasks = [asyncio.create_task(construction_chain.ainvoke({"input":chunk_text})) for index, chunk_text in enumerate(texts)]
results = await asyncio.gather(*tasks)
print(f"Finished LLM extraction after: {datetime.now() - start}")
docs = [el.dict() for el in results]
for index, doc in enumerate(docs):
doc['chunk_id'] = encode_md5(texts[index])
doc['chunk_text'] = texts[index]
doc['index'] = index
for af in doc["atomic_facts"]:
af["id"] = encode_md5(af["atomic_fact"])
# 导入块/原子事实/关键元素
graph.query(import_query, params={"data": docs, "document_name": document_name})
# 在块之间创建NEXT关系
graph.query("""
MATCH (c:Chunk)<-[:HAS_CHUNK]-(d:Document)
WHERE d.id = $document_name
WITH c ORDER BY c.index
WITH collect(c) AS nodes
UNWIND range(0, size(nodes) -2) AS index
WITH nodes[index] AS start, nodes[index + 1] AS end
MERGE (start)-[:NEXT]->(end)
""", params={"document_name":document_name})
print(f"Finished import at: {datetime.now() - start}")
开始提取:
await process_document(text, "wse2", chunk_size=2000, chunk_overlap=100)
现在去Neo4j中查看提取结果。可以看到每个节点周围有很多关联节点和事实文本,这就是数据核心。高分RAG都是从这些高质量数据开始的。这些数据的token长度基本在10~30 token,就是提取出来的简短语句加上节点。
再看看连接最多的5个节点,可以直观掌握知识图谱的概貌。目前已经按“关键节点+事实文本”建模完成,下一步就是接入Agent,让Agent自主探索这份数据。
因为篇幅限制,我们明天继续。这个系列价值非常高,希望每个人都能掌握。
