微软Phi-4-mini小模型在GraphRAG中大放异彩背后的技术解析
微软近期开源了一款轻量级模型——Phi-4-mini,其参数量仅为38亿,专为文本理解与生成任务设计。令人关注的是,这款小模型在数学与编程任务上的表现,足以与两倍参数量规模的模型相抗衡。为验证这一说法,我们设计了一组对比实验——在GraphRAG环境下,将Phi-4-mini与Phi-4、DeepS
微软近期开源了一款轻量级模型——Phi-4-mini,其参数量仅为38亿,专为文本理解与生成任务设计。令人关注的是,这款小模型在数学与编程任务上的表现,足以与两倍参数量规模的模型相抗衡。为验证这一说法,我们设计了一组对比实验——在GraphRAG环境下,将Phi-4-mini与Phi-4、DeepSeek-R1 7B等更大体量的模型进行横向测试。
实验过程中发现一个问题:Phi-4-mini默认构建的知识图谱相比其他模型明显更简单,且大多数图谱呈现碎片化状态,无法形成有效连接。这意味着,若要让这款小模型在特定领域真正发挥效用,必须对知识图谱的构建流程进行针对性优化,从而为生成任务提供更精准的上下文。特别是当模型需要处理训练数据中未曾包含的领域信息时,这一优化尤为关键。
接下来,我们将借助LlamaIndex框架,在一个本地化的GraphRAG环境中,为Phi-4-mini量身定制知识图谱的提取与检索流程。具体应用场景围绕某款网络性能监控(NPM)设备的REST API服务使用说明展开。为了高效托管这些知识图谱,Neo4j是理想之选——它作为企业级图数据库管理系统,界面直观且可视化能力强大,能帮助我们快速洞察数据背后的关联。
先来梳理一下所采用的技术栈。
1.0 技术栈概览
实验运行在一台MacBook M4 Pro上,系统为macOS Sequoia,配备16核GPU与24GB内存。Python版本选用3.12.9,建议保持版本一致以避免兼容性问题。
知识图谱的存储与查询自然交由Neo4j负责。它的优势在于生产级稳定性、易用性以及出色的可视化能力——这对于理解知识图谱的构建效果尤为重要。之前一篇文章已详细讲解过Neo4j的安装与配置步骤,本次直接沿用相同流程,部署了最新版Neo4j 2025.01.0。
最佳实践是先创建并激活一个虚拟环境:
python3.12 -m venv kg_qa
source kg_qa/bin/activate
与近期的其他工作类似,我们选用LlamaIndex框架,它提供了从数据获取到索引、检索、查询的全套模块与工具。其中PropertyGraphIndex模块简化了从非结构化文本构建知识图谱以及后续检索的流程。为了让Neo4j作为图存储后端,我们使用了Neo4jPropertyGraphStore模块和neo4j Python驱动程序。
本地语言模型(LLM)借助llama-cpp-python包来托管,它提供了llama.cpp的Python绑定。本次选用的模型是量化8位的Phi-4-mini Instruct版本。
以下是安装所需库的pip install命令:
pip install llama-index llama-index-readers-file llama-index-embeddings-huggingface
pip install neo4j llama-index-graph-stores-neo4j
CMAKE_ARGS="-DLLAMA_METAL=on" FORCE_CMAKE=1 pip install --upgrade --force-reinstall llama-index-llms-llama-cpp
过去的工作中,我们使用PropertyGraphIndex.from_documents配合默认的KG提取器与默认提取提示词,发现默认设置对DeepSeek-R1 7B效果良好,但对Phi-4系列模型则不太适用。具体到Phi-4-mini,生成的图谱规模小、碎片化,难以形成网状连接。图1展示了Phi-4-mini与DeepSeek-R1处理相同文档时构建的知识图谱对比——后者的图谱连接良好,甚至具备多跳深度路径;而Phi-4-mini的图谱不仅层级较浅,还混入了一些无关实体。

接下来,我们将探讨如何为Phi-4-mini定制知识图谱的构建与检索流程,使其真正发挥出应有的性能。
2.0 定制KG提取器与检索器
要构建一个完整的问答系统工作流,需要完成三个环节:包含KG提取器的知识获取流程、包含检索器的KG查询引擎,以及利用检索到的上下文进行LLM提示工程。
首先,导入所有必要的库:
from llama_index.core import (
PropertyGraphIndex,
SimpleDirectoryReader,
StorageContext,
Settings,
)
from llama_index.llms.llama_cpp import LlamaCPP
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
from llama_index.graph_stores.neo4j import Neo4jPropertyGraphStore
from llama_index.core.indices.property_graph import (
SimpleLLMPathExtractor,
LLMSynonymRetriever,
VectorContextRetriever,
)
import re
接着,实例化LlamaCPP对象,指定本地模型路径。同时实例化HuggingFaceEmbedding嵌入模型。为了让LlamaIndex各模块全局使用该LLM与嵌入模型,我们将Settings类的llm属性设为此LLM实例,embed_model属性设为HuggingFaceEmbedding对象。然后,实例化Neo4jPropertyGraphStore对象,配置由Neo4j数据库支持的图存储。代码如下:
# 加载本地模型
model_path='./models/Phi-4-mini-instruct.Q8_0.gguf'
llm = LlamaCPP(
model_path=model_path,
temperature=0.0,
max_new_tokens=2000,
context_window=5096,
model_kwargs={"n_gpu_layers": -1}, # 使用所有GPU层
verbose=False
)
embed_model = HuggingFaceEmbedding()
# 将llm和嵌入模型对象关联到Settings的属性
Settings.llm = llm
Settings.embed_model = embed_model
Settings.chunk_size = 512 # 设置文本块大小
# 初始化图数据库连接
url = "bolt://localhost:7687"
username = "neo4j"
password = "password" # 请替换为你的密码
database = "neo4j"
graph_store = Neo4jPropertyGraphStore(
username=username,
password=password,
url=url,
database=database,
)
2.1 KG构建
在LlamaIndex中,KG构建通过对每个文本块(chunk)应用一个或多个kg_extractors来实现,提取出的实体与关系会作为元数据附加到每个节点上。默认情况下使用两个提取器,其中包含SimpleLLMPathExtractor模块。该提取器通过解析短句中的单跳路径,生成“主语、谓语、宾语”形式的三元组。为了确保其最佳运行,需要根据Phi-4-mini的格式以及我们的应用领域来调整提示词。
不同LLM的特性差异较大,为了让Phi-4-mini准确理解我们的意图,必须对提取提示词进行定制。以下是我们定制的提取提示词:
extract_prompt = (
"下面提供了一些文本。根据这些文本,提取最多 "
"{max_paths_per_chunk} "
"个“主语、谓语、宾语”形式的知识三元组。\n"
"请避免使用停用词。\n"
"---------------------\n"
"示例:\n"
"文本: Alice is Bob's mother.\n"
"三元组:\nAlice, is mother of, Bob\n"
"文本: API requests must include an Authorization header with an access token preceded by "
"word 'Bearer', which expires after 15 minutes.\n"
"三元组:\n"
"API request, requires, Authorization header\n"
"Authorization header, contains, access token\n"
"Access token, preceded by, word 'Bearer'\n"
"Access token, expires after, 15 minutes\n"
"---------------------\n"
"文本: {text}\n"
"三元组:\n"
)
# 包装成Phi-4-mini的提示格式
ep = f'<|system|>You are a helpful AI assistant.<|end|><|user|>{extract_prompt}<|end|><|assistant|>'
当该提示词输入Phi-4-mini时,模型通常会生成一个带编号的三元组列表。为确保这些三元组符合KG构建所需格式,我们定义了一个parse_fn函数来解析响应:
def parse_fn(response_str: str) -> list[tuple[str, str, str]]:
# 按行分割
lines = response_str.strip().split("\n")
# 移除行首的编号和点号
lines = list(set([re.sub(r'^\d*\.\s*','',line) for line in lines if line.strip()]))
# 过滤掉格式不正确的行,并分割成包含3个元素(首字母大写)的列表
triplets = []
for line in lines:
parts = [item.strip().capitalize() for item in line.split(',') if item.strip()]
if len(parts) == 3:
triplets.append(tuple(parts))
return triplets
举例来说:如果模型返回"1. Alice, is mother of, Bob\n2. Alice, lives in, Wonderland",该函数会将其转换为[('Alice', 'is mother of', 'Bob'), ('Alice', 'lives in', 'Wonderland')]。函数中的第一个列表推导式生成一个去除了行首编号和多余空格的字符串列表,利用set去重可有效处理Phi-4-mini偶尔产生的重复三元组。之后,仅保留包含恰好两个逗号的字符串,按逗号分割,形成三元组,每个元素去除首尾空格并首字母大写。
有了适配Phi-4-mini格式的提示词和解析函数,即可实例化kg_extractor:
kg_extractor = SimpleLLMPathExtractor(
llm=llm, # 明确传递LLM实例
extract_prompt=ep,
parse_fn=parse_fn,
max_paths_per_chunk=10 # 每个chunk最多提取10个三元组
)
有了这个定制提取器,知识获取流程可以这样定义:
# 假设documents是已加载的文档列表
# documents = SimpleDirectoryReader("./data").load_data() # 例如
storage_context = StorageContext.from_defaults(property_graph_store=graph_store)
kg_index = PropertyGraphIndex.from_documents(
documents,
storage_context=storage_context,
max_triplets_per_chunk=10, # 与kg_extractor中的设置保持一致
kg_extractors=[kg_extractor],
embed_model=embed_model, # 明确传递嵌入模型
# show_progress=True # 可选:显示处理进度
)
接下来,继续定制上下文检索与查询流程。
2.2 KG检索与查询
默认情况下,LlamaIndex使用两个模块来辅助从KG中检索上下文:LLMSynonymRetriever和VectorContextRetriever。前者基于LLM生成的关键词或同义词列表进行检索,这正是我们可以针对所选LLM进行优化的地方。
下面是基于LlamaIndex默认检索提示词修改后,适配Phi-4-mini格式的检索提示词:
retrieve_prompt = (
"给定一个初始查询,请生成最多 {max_keywords} 个同义词或相关关键词,"
"考虑可能的大小写、复数形式、常用表达等。\n"
"请将所有同义词/关键词用 '^' 符号分隔:'keyword1^keyword2^...'\n"
"注意,结果应在单行内,并用 '^' 符号分隔。\n"
"----\n"
"查询: {query_str}\n"
"----\n"
"关键词: "
)
# 包装成Phi-4-mini的提示格式
rp = f'<|system|>You are a helpful AI assistant.<|end|><|user|>{retrieve_prompt}<|end|><|assistant|>'
该提示词会生成一个由'^'分隔的单行同义词列表。Phi-4-mini有时会返回许多重复项,为帮助解析响应,我们定义如下解析函数:
def parse_fn_r(output: str) -> list[str]:
# 使用集合去重,并按'^'分割
matches = list(set(output.strip().split("^")))
# 去除空字符串,去除首尾空格,首字母大写
return [x.strip().capitalize() for x in matches if x.strip()]
该函数负责分割关键词、去重、去除空格,并统一将首字母大写,以便在LLMSynonymRetriever中用于KG检索。实例化如下:
synonym_retriever = LLMSynonymRetriever(
storage_context.property_graph_store, # 使用storage_context中的图存储
include_text=True, # 检索时包含关联的文本块
synonym_prompt=rp, # 使用定制的提示词
output_parsing_fn=parse_fn_r, # 使用定制的解析函数
max_keywords=10, # 最多生成10个同义词
# 检索到节点后,沿着关系路径探索的深度
path_depth=2,
)
由于Neo4j数据库也支持向量查询,我们可以使用VectorContextRetriever基于向量相似性检索节点,然后获取与这些节点相连的路径,作为第二个检索器:
vector_retriever = VectorContextRetriever(
storage_context.property_graph_store, # 使用storage_context中的图存储
include_text=True, # 检索时包含关联的文本块
similarity_top_k=2, # 检索最相似的2个节点
path_depth=1, # 探索1跳路径
embed_model=embed_model # 明确传递嵌入模型
)
检索器准备就绪后,我们定义包含它们的查询引擎:
kg_keyword_query_engine = kg_index.as_query_engine(
retriever_mode="keyword", # 指定使用关键词检索模式
sub_retrievers=[synonym_retriever, vector_retriever],
llm=llm # 明确传递LLM实例
)
查询引擎定义完毕,现在可以完成查询流程了:
query = '请说明处理使用DH密码套件的TLS流量解密时,应该使用哪个版本的AppResponse REST API?'
# 包装成Phi-4-mini的提示格式
llm_prompt = f'<|system|>You are a helpful AI assistant.<|end|><|user|>{query}<|end|><|assistant|>'
response = kg_keyword_query_engine.query(llm_prompt)
print(f'查询: {query}\n响应: {response.response}')
现在,一切就绪,进入实验环节。这种定制化对构建的知识图谱到底有多大影响?让我们一探究竟!
3.0 实验验证
首先,确保本地Neo4j数据库管理系统已启动并监听连接。本次测试采用了几份来自Riverbed Technology的AppResponse(一种基于数据包的网络和应用分析设备)的API文档。该设备从特定网络监控点捕获网络数据包,进行自动化分析并提供洞察。第一份API文档描述了认证工作流程,第二份文档讨论了如何使用某些API端点对特定类型的TLS流量进行解密。
为展示针对KG提取器和检索组件所建议的定制化带来的影响,我们比较了Phi-4-mini在定制前后的结果。为方便起见,将前者称为Phi-4-mini-default,后者(带有定制)称为Phi-4-mini-custom。我们将有选择地利用Neo4j的高级可视化功能来突出关键差异。
3.1 KG构建效果
在本测试中,Phi-4-mini-default和Phi-4-mini-custom分别使用相同的文档重复进行了5次KG构建实验。它们生成的图谱特征在多次运行中表现出高度一致性。图2展示了使用Neo4j Browser可视化的构建结果。差异非常明显。定制后,知识图谱的最长路径长度显著增加。识别出的实体都与文档直接相关,不像使用默认设置时看到的一些无关实体。Phi-4-mini-custom还避免了因大小写问题导致的重复实体——这得益于我们在parse_fn函数中将所有实体的首字母统一大写,避免了因大小写差异导致的重复实体。

3.2 响应生成对比
在本测试中,我们针对API文档提出了三个相同的问题。为体现定制化的影响,先展示使用默认LlamaIndex设置的Phi-4-mini的响应,然后展示采用建议定制后的同一模型的响应。
第一个问题:
Q1: 请说明处理使用DH密码套件的TLS流量解密时,应该使用哪个版本的AppResponse REST API?
Phi-4-mini-default的响应如下:

可以看出,默认设置下的Phi-4-mini并没有准确理解问题的意图。它回答了1.0版本,并给出了一个合理的解释,但对其响应的准确性表示怀疑。为理解它为何得出此结论,我们查看查询引擎检索到并用作上下文的三元组和文档块(通过以下代码查看):
for n,sn in enumerate(response.__dict__.get('source_nodes')):
print(f'{"/"*30} 上下文节点 #{n}: {"\"*30}:{sn.get_text()}')
下面是检索流程提供给本地模型的唯一源节点的截图:

而Phi-4-mini-custom则正确地回答了预期的1.1版本。然而,它同样只有一个源节点作为上下文,如下所示:

请注意,检索到的两个三元组及相关的文档块精确地指向了正确的上下文,帮助模型确定1.1是正确的API版本。
第二个问题:
Q2: 我有client_random和master_key的十六进制值。我想通过REST API将它们安全地发送到AppResponse设备,以解密服务器172.168.22.2上端口8443的TLS会话。如何使用curl实现这一点?
Phi-4-mini-default的响应如下:

红色高亮部分指出了不准确之处。这个响应中的API端点是错误的。模型还将端口8443误认为是API服务监听的端口,而实际上它应该是需要解密的TLS流量的端口。此外,此API调用中缺少认证头(Authorization header),POST请求体中的JSON属性也缺失了。检索到的三元组和文档块与此问题无关,因此导致了模型的幻觉(hallucination)。
相反,Phi-4-mini-custom的响应如下:

这里的curl命令选项几乎是完整的,JSON请求体也完全正确。尽管被处理的文档中没有包含演示如何指定服务器端口的示例,但当提供了正确的上下文时,模型能够推断出来。然而,这里同样缺少认证头,并且API端点路径的最后一部分是错误的。对于这次生成,模型获得了四个源节点作为上下文,而Phi-4-mini-default只有一个。前者的第一个源节点包含了以下与问题直接相关的三元组和文本块:

结合其余的相关上下文,模型得以生成一个近乎完整的响应。
第三个问题(较长,要求先认证再执行操作):
Q3: 一个负载均衡器和一台AppResponse设备都能看到相同的、使用DH密码套件的TLS会话。对于其中一个TLS会话,我想将它的client_random值3c555b83e8db003fc35a4a5394e566c3234e2d325213b3a3a82ab2d651c8a151和master_key 3142a6a50f8e1dc17f2b22644464820cf59edb7792f0e36bc3dca4aa3fb5b99dbed7e2d00a2924ab从负载均衡器发送到AppResponse,以便它解密此会话。首先,提供一个curl命令,使用账户用户名/密码向IP地址为10.1.1.123的AppResponse设备执行API调用身份验证以获取访问令牌。然后,提供第二个curl命令,将给定的密钥安全地以JSON格式发送到AppResponse。
Phi-4-mini-default的响应如下:

它正确地生成了两条用于IP地址为10.1.1.123的设备的curl命令。但是,第一个命令的API端点是错误的,其用于身份验证的JSON对象结构也不正确。
现在看看Phi-4-mini-custom的响应:

这个响应几乎完全正确,并且编号清晰。身份验证请求使用了正确的JSON对象结构。发送密钥的第二个命令也完全正确。然而,值得注意的是,它选择了不安全的HTTP端点,而非HTTPS,这与提示中要求的安全传输及其自身声明的安全传输目标存在矛盾。另一个小错误是身份验证API端点,应该是/api/mgmt.aaa/1.0/token才对。
为理解Phi-4-mini-custom为何能基本正确地给出第一个curl命令的选项,我们检查了它的上下文。与Phi-4-mini-default的检索器只返回一个源节点不同,Phi-4-mini-custom的检索器返回了6个源节点!并且第一个源节点就包含了预期的格式,如下所示:

第一个三元组 (Api calls, Require, Authentication) 与查询的第一部分匹配。此外,第二个和第三个三元组似乎是从实体Api calls出发的第二跳关系。如果你还记得之前的实现部分,LLMSynonymRetriever的path_depth属性控制着这种行为。
为帮助我们更好地理解第一个源节点的选择过程,让我们利用Neo4j的一些高级可视化功能。在Neo4j Browser中,如果我们使用Cypher查询MATCH (a)-[r]-(b) WHERE a.name='Api calls' RETURN r,a,b来查找从实体Api calls出发的三元组,结果图如图3所示。

图3中的红色节点代表提及这两个实体的文档块。选中这个红色节点后,该图右侧的“节点属性”面板显示了之前提供的源节点0中的部分内容。要获取从Api calls出发最多2跳的实体,我们可以使用Cypher查询MATCH (a)-[r*1..2]-(b) WHERE a.name = 'Api calls' RETURN r,a,b。结果图如图4所示。源节点0中剩余的两个三元组代表了该图底部附近的1跳路径。同样,所有这三个三元组都由同一个文本块引用,因此该文本块作为相关文本被包含在内。

相比之下,Phi-4-mini-default再次只有一个源节点作为上下文,包含三元组 (Philz, Uses, Client handshake secret) 和 (Philz, Uses, Server handshake secret),其主语是无关的。宾语则在一个讨论用于发送PFS解密密钥的端点的文本块中被提及。因此,这导致模型生成了针对我们问题第二部分的正确命令。
从以上三个示例中可以清楚地看到高质量知识图谱的影响。通过从给定问题中选择同义词,Phi-4-mini-custom的同义词检索器能够从这些知识图谱中找到相关的三元组以及关联的文档块,从而为语言模型提供正确的上下文以改进其响应。此外,由于图谱的连接性更好,检索器能够持续地为该模型找到更多相关的上下文,从而带来更好的生成结果。
4.0 总结与展望
微软推出的Phi-4-mini是一款面向资源受限环境的小型语言模型。对于一个小模型而言,它拥有不错的生成性能。要将像Phi-4-mini这样的公开模型应用于特定细分领域,一定程度的适配是必要的。GraphRAG是一种流行的方法,它将文档内容提取并表示为知识图谱,后续通过检索这些图谱来为模型的生成提供上下文依据,同时减少幻觉现象。为了高效地处理这些图谱,我们采用了Neo4j图数据库管理系统。作为一个额外的好处,Neo4j的Web UI支持丰富的查询语言和出色的可视化功能,使我们能够深入洞察数据。
在本文中,我们探讨了如何定制LlamaIndex的知识获取和检索流程以支持Phi-4-mini。对于获取流程,我们调整了SimpleLLMPathExtractor模块的提取提示词和响应解析函数;对于检索流程,我们以类似的方式修改了LLMSynonymRetriever的相同方面。通过前者的改动,生成的知识图谱连接性大大增强,包含了更多与领域相关的有意义的实体和关系。有了这些更高质量的图谱,检索质量以及为支持生成而找到的相关源节点的数量都得到了持续改善。
通过这种GraphRAG设置,我们发现Phi-4-mini能够相当出色地生成用于访问NPM设备REST API服务的完整curl命令。虽然定制化显著降低了幻觉现象,但模型在某些信息推理上仍有改进空间。
你是一名 AI 行业编辑,请围绕下面这条热点输出一份资讯解读:
热点:微软Phi-4-mini小模型在GraphRAG中大放异彩背后的技术解析要求:
1. 先用一句话解释这条热点在讲什么
2. 再总结它为什么重要
3. 说明会影响哪些 AI 产品或内容方向
4. 最后给出 3 个适合资讯站使用的标题
游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。
相关热点Dzine是一款强调构图控制与风格管理的AI图像设计工具,提供样式库、图层操作、定位和素描工具,支持文生图与图生图,具备生成填充编辑、一键修复增强及最高6144像素超高清导出功能,降低设计门槛,兼顾新手与专业用户。
3D虚拟空间的搭建,过去往往依赖专业建模软件和大量手动操作,技术门槛相当高。但现在,一款名为Arrival的云端SaaS解决方案正凭借AI与拖放功能,将这件事变得像搭积木一样轻松便捷。 什么是Arrival? Arrival本质上是一套专业的软件工具,核心目标就是帮助用户快速构建一个3D虚拟空间。它
ZENAI通过AI自动完成用户访谈,省去人工招募与主持流程,并自动总结用户场景、痛点及人物画像。产品经理、设计师、研究员可借此快速验证假设、提炼场景、获取市场洞察,加速产品市场契合度(PMF)达成,提供基础与专业两种套餐。
MeshcapadeMe基于SMPL人体模型技术,提供API接口支持图像、视频、测量及3D扫描输入,自动生成统一格式的逼真数字分身,无需专业建模技能即可将各类素材转化为可动画、跨平台使用的数字人类,适用于虚拟现实、游戏与影视等领域。
- 日榜
- 周榜
- 月榜
热点快看
