基于语义内核(Semantic Kernel)与 Neo4j 构建轻量级知识问答系统

5 阅读4分钟

引言

随着大语言模型(LLM)技术的飞速发展,结合结构化知识库构建智能问答系统成为当前 AI 应用的重要方向。本文将介绍如何利用 Microsoft Semantic Kernel(SK)Neo4j 图数据库 快速搭建一个轻量级、可解释、且具备上下文感知能力的问答系统。

该方案的优势在于:

  • 利用 SK 简化 LLM 调用与插件集成;
  • 借助 Neo4j 高效存储和查询实体关系;
  • 实现“先查知识库,再生成答案”的可控推理流程。

技术栈简介

1. Semantic Kernel(SK)

Semantic Kernel 是微软推出的轻量级 SDK,支持将自然语言函数(prompt functions)、传统代码函数(native functions)以及向量记忆(memory)统一编排,便于构建 LLM 应用。它支持 Python 和 C#,本文以 Python 为例。

2. Neo4j

Neo4j 是领先的原生图数据库,擅长处理高度关联的数据。在知识图谱场景中,节点表示实体(如人物、地点),边表示关系(如“出生于”、“就职于”),非常适合用于事实型问答。


系统架构概览

用户提问
   ↓
[语义解析] → 提取关键实体/意图(可选)
   ↓
[Neo4j 查询] → 根据实体检索相关子图
   ↓
[结果拼接] → 将结构化数据转为自然语言上下文
   ↓
[SK + LLM] → 结合上下文生成最终回答

实现步骤

步骤一:准备 Neo4j 知识图谱

假设我们构建一个简单的公司员工知识图谱:

CREATE (a:Person {name: "张三", role: "工程师"})
CREATE (b:Person {name: "李四", role: "产品经理"})
CREATE (c:Company {name: "星辰科技"})
CREATE (a)-[:WORKS_AT]->(c)
CREATE (b)-[:WORKS_AT]->(c)
CREATE (a)-[:REPORTS_TO]->(b)

步骤二:编写 Neo4j 查询插件(Native Function)

在 SK 中注册一个本地函数,用于根据问题中的关键词查询 Neo4j:

from semantic_kernel.functions.kernel_function_decorator import kernel_function
from neo4j import GraphDatabase

class Neo4jPlugin:
    def __init__(self, uri, user, password):
        self.driver = GraphDatabase.driver(uri, auth=(user, password))

    @kernel_function(
        description="根据人名查询其工作信息和汇报关系",
        name="query_employee_info"
    )
    def query_employee_info(self, name: str) -> str:
        with self.driver.session() as session:
            result = session.run("""
                MATCH (p:Person {name: $name})-[r]->(target)
                RETURN type(r) AS rel, target.name AS target_name, target.role AS target_role
                UNION
                MATCH (p:Person {name: $name})<-[:WORKS_AT]-(comp:Company)
                RETURN 'WORKS_AT' AS rel, comp.name AS target_name, null AS target_role
            """, name=name)
            facts = []
            for record in result:
                if record["rel"] == "REPORTS_TO":
                    facts.append(f"{name} 汇报给 {record['target_name']}{record['target_role']})")
                elif record["rel"] == "WORKS_AT":
                    facts.append(f"{name}{record['target_name']} 工作")
            return "; ".join(facts) if facts else "未找到相关信息"

步骤三:配置 Semantic Kernel 并集成插件

import semantic_kernel as sk
from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion

kernel = sk.Kernel()
kernel.add_service(OpenAIChatCompletion(
    service_id="openai",
    ai_model_id="gpt-3.5-turbo",
    api_key="YOUR_API_KEY"
))

# 注册插件
neo4j_plugin = Neo4jPlugin("bolt://localhost:7687", "neo4j", "password")
kernel.import_plugin_from_object(neo4j_plugin, "Neo4jPlugin")

步骤四:定义语义函数(Prompt Template)

创建一个 SK 语义函数,引导 LLM 利用插件返回的信息作答:

# ask_about_employee.skprompt.txt
你是一个公司内部助手。请根据以下事实回答问题:

{{Neo4jPlugin.query_employee_info $input}}

问题:{{$input}}
回答应简洁、准确,仅基于上述事实。

加载并调用:

prompt_template = """
你是一个公司内部助手。请根据以下事实回答问题:

{{Neo4jPlugin.query_employee_info $input}}

问题:{{$input}}
回答应简洁、准确,仅基于上述事实。
"""

ask_function = kernel.create_function_from_prompt(
    prompt_template,
    function_name="ask_about_employee",
    plugin_name="CompanyQA"
)

# 用户提问
question = "张三的上级是谁?"
answer = await kernel.invoke(ask_function, input=question)
print(answer)

效果示例

输入

“张三在哪家公司工作?”

Neo4j 返回

“张三 在 星辰科技 工作”

LLM 输出

张三在星辰科技工作。

输入

“李四负责什么?”

Neo4j 返回

“李四 在 星辰科技 工作;张三 汇报给 李四(产品经理)”

LLM 输出

李四是产品经理,负责管理工程师张三,任职于星辰科技。


优势与扩展方向

可控性强:答案基于真实图谱数据,避免幻觉。
可解释性好:每一步查询可追溯。
易于扩展:可加入向量搜索(SK Memory)处理模糊查询,或接入更多数据源。

未来优化

  • 使用 NER 模型自动提取问题中的实体;
  • 支持多跳关系查询(如“张三上级的公司”);
  • 结合 RAG 架构融合非结构化文档。

结语

通过 Semantic Kernel 与 Neo4j 的结合,我们能够快速构建一个兼具灵活性与可靠性的问答系统。这种“结构化知识 + 语言模型”的混合架构,是迈向企业级可信 AI 应用的重要一步。