各位同仁,女士们,先生们,下午好!
今天,我们齐聚一堂,探讨一个在构建智能Agent时至关重要且充满挑战的议题:如何实现Agent知识的永久性更新,特别是当用户纠正了Agent的错误之后。 想象一下,您正在与一个Agent交流,它犯了一个事实性错误,而您及时指正了它。我们期望这个Agent能从这次纠正中学习,并且在未来,无论是与您还是与任何其他用户交互时,都能避免重蹈覆辙。这不仅仅是修复一个Bug,更是Agent智能进化之路上的关键一步。
当前的大型语言模型(LLMs)固然强大,但它们的核心知识库在训练完成后是相对静态的。它们擅长基于预训练数据进行泛化和推理,但对于个体用户在会话中的实时反馈和纠正,并不能直接、永久地修改其内部参数。这就引出了我们今天讨论的核心:如何构建一套“知识更新逻辑”,让Agent能够将用户的纠正信息,有效地、永久地融入其长期记忆库。
我们将从Agent的现有架构和挑战出发,逐步深入探讨多种知识更新策略,包括基于检索增强生成(RAG)的动态知识库、知识图谱、规则系统,乃至更深层次的模型微调。我将结合具体的代码示例,展示这些策略在实践中如何落地,并讨论它们各自的优缺点及适用场景。
第一章:Agent的记忆与挑战——为何知识更新如此复杂?
在深入探讨解决方案之前,我们首先需要理解Agent的“记忆”是如何工作的,以及为何直接更新其知识如此困难。
1.1 Agent的记忆层次
一个典型的智能Agent通常具备多层次的记忆:
- 短期记忆(Short-term Memory/Context Window):这是LLM在当前会话中能够访问的信息。它包括用户当前的输入、Agent之前的回复,以及可能通过工具调用获取的临时数据。这些信息通常存储在LLM的上下文窗口中,是暂时的,会随着会话的结束而消失。
- 长期记忆(Long-term Memory):这是Agent持久化的知识库,不随会话结束而清空。它可能包括:
- 预训练模型参数:LLM模型本身携带的通用世界知识。
- 外部知识库:通过RAG机制访问的文档、数据库、知识图谱等。
- 学习到的用户偏好或行为模式:通过用户交互积累的个性化信息。
我们的目标,正是要将用户纠正的知识,永久地写入这个长期记忆层。
1.2 LLM的静态性与挑战
LLMs在训练完成后,其数十亿甚至上万亿的参数就固定了。每次推理,它们都是基于这些固定的参数来生成响应。这种静态性带来了几个核心挑战:
- 灾难性遗忘(Catastrophic Forgetting):如果直接尝试对模型进行增量训练以加入新知识,模型往往会忘记之前学到的知识,特别是在数据量小、训练不当的情况下。
- 计算成本与实时性:对一个大型LLM进行一次完整的微调(Fine-tuning)是极其昂贵和耗时的。我们不可能每次用户纠正都进行一次模型微调,这不符合实时更新的需求。
- 数据质量与安全:用户提供的纠正信息可能存在错误、偏见或不安全内容。未经筛选和验证,直接将其注入模型可能会引入新的问题。
- 知识的粒度与结构:用户纠正通常是自然语言形式,如何将其转化为模型可理解、可利用的结构化知识,是一个难题。
因此,我们需要设计巧妙的外部机制,在不直接修改LLM核心参数的前提下,实现知识的永久性更新。
第二章:知识更新的检测与提取
在更新知识之前,我们首先需要识别出用户何时进行了纠正,并从中提取出有效的知识。
2.1 纠正事件的识别
用户纠正 Agent 错误的方式多种多样,可以分为显式纠正和隐式纠正。
- 显式纠正:用户明确指出Agent的错误。
- “不对,你错了,A不是B,而是C。”
- “你给的信息是旧的,最新情况是D。”
- “我刚才问的那个问题,正确答案应该是E。”
- 隐式纠正:用户通过提供新的、与Agent之前回答不符的信息来修正。
- Agent:“北京的首都是上海。” 用户:“实际上,中国的首都是北京。” (用户未直接说Agent错了,但提供了正确信息)
- 用户对Agent的回答进行评分(如点踩),然后提供了一个替代答案。
实现方式:
- 关键词与正则表达式:简单直接,但覆盖范围有限,容易误判。
- 语义相似性与意图识别:通过训练一个小型分类模型或使用LLM本身来判断用户输入是否包含“纠正”意图。
- LLM辅助判断:让LLM作为一个“纠正检测器”。
代码示例:基于LLM的纠正检测
我们可以构建一个Prompt,让LLM判断用户输入是否为纠正。
import os
from openai import OpenAI # 假设使用OpenAI API
# 假设您已设置 OPENAI_API_KEY 环境变量
# client = OpenAI()
class CorrectionDetector:
def __init__(self, llm_client=None):
# 实际应用中可以传入不同的LLM客户端
self.llm_client = llm_client if llm_client else OpenAI()
def detect_correction(self, user_input: str, agent_previous_response: str) -> bool:
"""
使用LLM判断用户输入是否是在纠正Agent之前的响应。
Args:
user_input (str): 用户的最新输入。
agent_previous_response (str): Agent的上一个响应。
Returns:
bool: 如果是纠正,返回True;否则返回False。
"""
prompt = f"""
你是一个智能助手,负责判断用户是否在纠正你之前的回答。
请分析以下对话片段,并判断用户的最新输入是否包含对Agent之前回答的纠正。
如果用户是在纠正Agent的错误信息、提供更准确的信息、指出Agent的不足,请回答“是”。
如果用户只是继续提问、表达感谢、提出新的问题或与Agent无关的评论,请回答“否”。
Agent之前的回答: "{agent_previous_response}"
用户的最新输入: "{user_input}"
请直接回答“是”或“否”。
"""
try:
response = self.llm_client.chat.completions.create(
model="gpt-3.5-turbo", # 或其他合适的LLM模型
messages=[
{"role": "system", "content": "你是一个判断用户意图的智能助手。"},
{"role": "user", "content": prompt}
],
max_tokens=5,
temperature=0
)
decision = response.choices[0].message.content.strip().lower()
return decision == "是"
except Exception as e:
print(f"Error during correction detection: {e}")
return False # 默认不认为是纠正,或根据业务逻辑处理
# 示例使用
if __name__ == "__main__":
detector = CorrectionDetector()
# 示例1:显式纠正
agent_resp_1 = "北京的首都是上海。"
user_input_1 = "不对,你错了,北京是中国的首都,不是上海。"
print(f"'{user_input_1}' 是否是纠正? {detector.detect_correction(user_input_1, agent_resp_1)}")
# 示例2:隐式纠正
agent_resp_2 = "太阳围绕地球转。"
user_input_2 = "根据开普勒定律,地球围绕太阳转。"
print(f"'{user_input_2}' 是否是纠正? {detector.detect_correction(user_input_2, agent_resp_2)}")
# 示例3:非纠正
agent_resp_3 = "好的,还有什么可以帮助您的吗?"
user_input_3 = "谢谢,暂时没有了。"
print(f"'{user_input_3}' 是否是纠正? {detector.detect_correction(user_input_3, agent_resp_3)}")
agent_resp_4 = "Python是一种流行的编程语言。"
user_input_4 = "那它主要用于哪些领域呢?"
print(f"'{user_input_4}' 是否是纠正? {detector.detect_correction(user_input_4, agent_resp_4)}")
2.2 知识的提取与结构化
一旦确认是纠正,下一步就是从用户输入中提取出具体的、可用于更新的知识点。这通常意味着要从非结构化的自然语言中识别出事实、实体、关系,以及被纠正的旧信息和正确的新信息。
实现方式:
- 命名实体识别(NER)与关系抽取(RE):使用NLP模型识别实体(如人名、地点、组织)和它们之间的关系。
- LLM辅助抽取:再次利用LLM的强大文本理解能力,通过特定的Prompt来结构化提取信息。
代码示例:基于LLM的知识点抽取
我们可以设计一个Prompt,让LLM以JSON格式输出提取的知识。
import json
class KnowledgeExtractor:
def __init__(self, llm_client=None):
self.llm_client = llm_client if llm_client else OpenAI()
def extract_correction_knowledge(self, user_input: str, agent_previous_response: str) -> dict:
"""
使用LLM从用户纠正中提取结构化知识。
Args:
user_input (str): 用户的最新输入,包含纠正信息。
agent_previous_response (str): Agent的上一个响应,包含被纠正的信息。
Returns:
dict: 包含旧知识、新知识、相关实体和纠正类型等信息的字典。
"""
prompt = f"""
你是一个智能助手,负责从用户对Agent错误的纠正中提取结构化知识。
Agent之前的错误回答: "{agent_previous_response}"
用户的纠正输入: "{user_input}"
请从用户的纠正中提取以下信息,并以JSON格式返回。
如果某个信息不存在,请使用null。
JSON格式要求:
{{
"correction_type": "事实错误" | "信息过时" | "不完整信息" | "其他",
"old_incorrect_fact": "Agent之前错误的事实描述",
"new_correct_fact": "用户提供的正确的事实描述",
"entity": "纠正涉及的核心实体或概念",
"attribute": "纠正涉及的实体的属性或特征",
"value_before_correction": "Agent错误回答中关于属性的值",
"value_after_correction": "用户纠正后关于属性的正确值",
"context_keywords": ["与纠正相关的其他关键词"]
}}
示例1:
Agent之前的错误回答: "北京的首都是上海。"
用户的纠正输入: "不对,你错了,北京是中国的首都,不是上海。"
输出:
{{
"correction_type": "事实错误",
"old_incorrect_fact": "北京的首都是上海",
"new_correct_fact": "北京是中国的首都",
"entity": "北京",
"attribute": "首都",
"value_before_correction": "上海",
"value_after_correction": "中国",
"context_keywords": ["中国", "首都"]
}}
示例2:
Agent之前的错误回答: "2020年全球GDP最高的国家是美国。"
用户的纠正输入: "不是2020年,2023年全球GDP最高的国家仍然是美国。"
输出:
{{
"correction_type": "信息过时",
"old_incorrect_fact": "2020年全球GDP最高的国家是美国",
"new_correct_fact": "2023年全球GDP最高的国家是美国",
"entity": "美国",
"attribute": "全球GDP最高年份",
"value_before_correction": "2020年",
"value_after_correction": "2023年",
"context_keywords": ["全球GDP", "国家"]
}}
请开始:
"""
try:
response = self.llm_client.chat.completions.create(
model="gpt-3.5-turbo", # 推荐使用更强大的模型如 gpt-4 进行知识抽取
messages=[
{"role": "system", "content": "你是一个将用户纠正信息结构化的智能助手。"},
{"role": "user", "content": prompt}
],
response_format={"type": "json_object"}, # 确保输出是JSON
temperature=0
)
extracted_data = json.loads(response.choices[0].message.content)
return extracted_data
except json.JSONDecodeError as e:
print(f"Error decoding JSON from LLM: {e}")
return {"error": "JSON decoding failed"}
except Exception as e:
print(f"Error during knowledge extraction: {e}")
return {"error": str(e)}
# 示例使用
if __name__ == "__main__":
extractor = KnowledgeExtractor()
# 示例1:事实错误
agent_resp_1 = "Python是由Guido van Rossum在1999年发明的。"
user_input_1 = "不对,Python是Guido van Rossum在1991年左右发布的。"
extracted_1 = extractor.extract_correction_knowledge(user_input_1, agent_resp_1)
print("n--- 示例1 提取结果 ---")
print(json.dumps(extracted_1, indent=2, ensure_ascii=False))
# 示例2:信息过时
agent_resp_2 = "目前全球最高建筑是台北101。"
user_input_2 = "你说的过时了,现在全球最高建筑是迪拜的哈利法塔。"
extracted_2 = extractor.extract_correction_knowledge(user_input_2, agent_resp_2)
print("n--- 示例2 提取结果 ---")
print(json.dumps(extracted_2, indent=2, ensure_ascii=False))
# 示例3:更复杂的纠正
agent_resp_3 = "太阳系只有八大行星,没有冥王星。"
user_input_3 = "你说的没错,但更准确的说法是冥王星被重新分类为矮行星,所以太阳系现在主要有八大行星。"
extracted_3 = extractor.extract_correction_knowledge(user_input_3, agent_resp_3)
print("n--- 示例3 提取结果 ---")
print(json.dumps(extracted_3, indent=2, ensure_ascii=False))
第三章:永久性知识更新策略
有了识别和提取机制,接下来就是如何将这些结构化的纠正信息永久地存储到Agent的长期记忆中。我们将探讨几种主流策略。
3.1 策略一:基于检索增强生成(RAG)的动态知识库
RAG是目前最常用且有效的Agent增强方法之一。其核心思想是为Agent配备一个可查询的外部知识库(通常是向量数据库),在生成回复时,Agent会先从这个知识库中检索相关信息,然后结合检索到的信息和LLM的自身能力来生成最终答案。
更新逻辑:当用户纠正Agent时,我们将纠正后的正确信息作为一个新的“文档”或更新现有“文档”,将其嵌入(embedding)后存储到向量数据库中。
工作流程:
- 检测与提取:如前所述,识别用户纠正,并提取出
new_correct_fact、entity、attribute等信息。 - 构建知识块:将提取出的正确信息构造成一个新的文本块(chunk),例如:“关于[实体],[属性]的正确值是[正确值]。”
- 生成嵌入:使用与RAG系统相同的嵌入模型,将新知识块转换为向量。
- 存储到向量数据库:将新知识块的文本内容和其向量存储到向量数据库中,并可以附带元数据(如纠正时间、用户ID、纠正来源、旧知识ID等)。
- 检索优先:在Agent未来的查询中,如果与新知识块高度相关,向量数据库会优先检索到它,从而指导LLM生成正确的答案。
优点:
- 实时性:更新可以非常快,一旦入库即可生效。
- 成本效益:无需重新训练LLM,成本较低。
- 可解释性:可以追溯Agent引用了哪个知识块来生成答案。
- 增量更新:可以方便地添加新知识,而不会影响已有知识。
挑战:
- 冲突解决:如果新知识与旧知识(或其他来源的知识)冲突,RAG本身难以判断哪个更权威。可能需要版本控制、置信度评分或人工审核。
- 知识粒度:过细或过粗的知识块都会影响检索效果。
- “遗忘”旧知识:RAG更多是“添加”知识,如何有效地“删除”或“抑制”旧的错误知识,是一个需要额外机制解决的问题。
代码示例:RAG动态知识库更新
我们将使用ChromaDB作为本地向量数据库,sentence-transformers作为嵌入模型(实际生产中可替换为OpenAI/Cohere等API)。
from langchain_community.document_loaders import TextLoader
from langchain_community.vectorstores import Chroma
from langchain_text_splitters import CharacterTextSplitter
from langchain_community.embeddings import SentenceTransformerEmbeddings
from langchain.chains import RetrievalQA
from langchain_openai import ChatOpenAI # 假设使用OpenAI LLM
import os
# 确保环境变量设置
# os.environ["OPENAI_API_KEY"] = "YOUR_OPENAI_API_KEY"
class DynamicRAGKnowledgeBase:
def __init__(self, persist_directory="./chroma_db"):
# 初始化嵌入模型
self.embeddings = SentenceTransformerEmbeddings(model_name="all-MiniLM-L6-v2")
# 初始化或加载ChromaDB
self.persist_directory = persist_directory
self.vector_db = Chroma(persist_directory=self.persist_directory, embedding_function=self.embeddings)
print(f"RAG Knowledge Base initialized, persisting to: {self.persist_directory}")
# 初始化LLM用于检索QA
self.llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0.1)
self.qa_chain = RetrievalQA.from_chain_type(
llm=self.llm,
chain_type="stuff", # 将所有检索到的文档 stuffing 到 prompt 中
retriever=self.vector_db.as_retriever(search_kwargs={"k": 3}) # 检索前3个最相关的文档
)
def add_initial_knowledge(self, documents: list[str]):
"""添加初始知识文档"""
print("Adding initial knowledge...")
# 简单地将文本作为文档
self.vector_db.add_texts(documents=documents)
print("Initial knowledge added.")
def update_knowledge_from_correction(self, extracted_knowledge: dict):
"""
根据用户纠正提取的结构化知识更新RAG知识库。
Args:
extracted_knowledge (dict): 从KnowledgeExtractor获取的结构化纠正信息。
"""
new_correct_fact = extracted_knowledge.get("new_correct_fact")
entity = extracted_knowledge.get("entity")
attribute = extracted_knowledge.get("attribute")
value_after_correction = extracted_knowledge.get("value_after_correction")
old_incorrect_fact = extracted_knowledge.get("old_incorrect_fact")
if not new_correct_fact:
print("No 'new_correct_fact' found in extracted knowledge, skipping update.")
return
# 构造一个新的知识文档,包含纠正后的信息
# 可以设计更复杂的模板以提高检索效果
knowledge_chunk = f"用户纠正信息:关于 {entity or '某个实体'},其 {attribute or '某属性'} 的正确值是 {value_after_correction or new_correct_fact}。原错误信息为:{old_incorrect_fact}。"
if not entity and not attribute: # 如果没有提取到实体属性,直接使用new_correct_fact
knowledge_chunk = f"用户纠正信息:{new_correct_fact}。"
# 可以添加元数据,比如时间戳、来源等
metadata = {
"source": "user_correction",
"timestamp": extracted_knowledge.get("timestamp", "unknown"),
"entity": entity,
"attribute": attribute,
"old_fact": old_incorrect_fact
}
print(f"Updating RAG with new knowledge chunk: '{knowledge_chunk}' with metadata: {metadata}")
self.vector_db.add_texts(texts=[knowledge_chunk], metadatas=[metadata])
print("Knowledge base updated successfully.")
# 如何处理旧知识:
# 1. 简单地让新知识的向量“覆盖”旧知识的检索优先级 (当前实现)。
# 2. 如果向量数据库支持,可以尝试删除与 old_incorrect_fact 相关的旧文档。
# ChromaDB通过 id 删除,但这里的“旧知识”可能没有明确的ID。
# 更复杂的逻辑可能需要先检索出可能错误的旧文档ID,再删除。
# 例如:self.vector_db.delete(ids=[id_of_old_fact])
def query_knowledge(self, question: str) -> str:
"""从RAG知识库中查询并生成答案"""
print(f"nQuerying: '{question}'")
response = self.qa_chain.run(question)
print(f"RAG Response: {response}")
return response
# 示例使用
if __name__ == "__main__":
# 清理旧的DB,以便每次运行都是全新开始
if os.path.exists("./chroma_db"):
import shutil
shutil.rmtree("./chroma_db")
print("Cleaned up previous ChromaDB.")
rag_system = DynamicRAGKnowledgeBase()
detector = CorrectionDetector()
extractor = KnowledgeExtractor()
# 初始知识(可能包含错误)
rag_system.add_initial_knowledge([
"Python编程语言是由Guido van Rossum在1999年发明的。",
"地球围绕太阳转。",
"全球最高建筑是台北101。",
"中国的首都是上海。" # 这是一个错误的事实
])
# 首次查询,Agent可能给出错误答案
query_1 = "中国的首都是哪里?"
rag_system.query_knowledge(query_1)
# 用户纠正
agent_resp_to_correct = "中国的首都是上海。"
user_correction = "不对,你错了,中国的首都是北京。"
if detector.detect_correction(user_correction, agent_resp_to_correct):
print("n--- 检测到用户纠正 ---")
extracted_info = extractor.extract_correction_knowledge(user_correction, agent_resp_to_correct)
extracted_info["timestamp"] = "2023-10-27" # 添加时间戳作为元数据
print(json.dumps(extracted_info, indent=2, ensure_ascii=False))
rag_system.update_knowledge_from_correction(extracted_info)
else:
print("n--- 未检测到用户纠正 ---")
# 再次查询,验证知识是否更新
query_2 = "中国的首都是哪里?"
rag_system.query_knowledge(query_2)
# 另一个纠正示例
query_3 = "Python是什么时候发布的?"
rag_system.query_knowledge(query_3) # 可能会给出1999年
agent_resp_to_correct_2 = "Python编程语言是由Guido van Rossum在1999年发明的。"
user_correction_2 = "不对,Python是Guido van Rossum在1991年左右发布的。"
if detector.detect_correction(user_correction_2, agent_resp_to_correct_2):
print("n--- 检测到用户纠正 (Python发布时间) ---")
extracted_info_2 = extractor.extract_correction_knowledge(user_correction_2, agent_resp_to_correct_2)
extracted_info_2["timestamp"] = "2023-10-27-2"
print(json.dumps(extracted_info_2, indent=2, ensure_ascii=False))
rag_system.update_knowledge_from_correction(extracted_info_2)
else:
print("n--- 未检测到用户纠正 (Python发布时间) ---")
query_4 = "Python的发布年份是什么?"
rag_system.query_knowledge(query_4) # 应该给出1991年
3.2 策略二:知识图谱(Knowledge Graphs, KGs)
知识图谱以结构化的方式存储实体(Entities)、关系(Relations)和属性(Attributes),形成一个由节点和边构成的图。它能够清晰地表达复杂的语义关系,进行推理。
更新逻辑:当用户纠正Agent时,提取出的实体、关系、属性-值对可以直接用于在知识图谱中创建新节点、新边,或者修改现有节点/边的属性。
工作流程:
- 检测与提取:识别纠正,并重点提取
entity、attribute、value_before_correction、value_after_correction。 - 图谱操作:
- 如果
entity不存在,创建新节点。 - 如果
attribute是一个关系,创建(entity1)-[relation]->(entity2)的边。 - 如果
attribute是一个属性,更新entity节点的相应属性值。 - 对于旧的错误信息,可以将其标记为“已过时”或直接删除(如果业务允许)。
- 如果
- Agent查询:Agent在回答问题时,可以通过查询知识图谱获取结构化、准确的事实。
优点:
- 结构化与精确性:知识以高度结构化的形式存储,减少歧义。
- 推理能力:可以基于图谱进行多跳推理,发现隐藏关系。
- 冲突管理:更容易通过图谱的约束和推理规则来检测和解决冲突。
- 可解释性:知识路径清晰,易于解释Agent的推理过程。
挑战:
- 抽取复杂性:从自然语言中准确抽取实体、关系并映射到图谱模式(Schema)是困难的,通常需要更强大的NLP技术或专门的LLM提示工程。
- 模式演进:知识图谱的模式(Schema)需要随着新知识的加入而演进,这可能很复杂。
- 构建与维护成本:构建和维护大型知识图谱的工具和人力成本较高。
代码示例:知识图谱更新(使用networkx进行演示)
实际生产环境可能使用Neo4j、RDF三元组数据库等。这里用networkx简化演示核心逻辑。
import networkx as nx
import matplotlib.pyplot as plt # 用于可视化,但实际输出中不显示图片
class KnowledgeGraphSystem:
def __init__(self):
self.graph = nx.DiGraph() # 有向图
print("Knowledge Graph System initialized.")
def add_fact(self, subject: str, predicate: str, obj: str, source: str = "initial"):
"""
添加一个三元组事实 (subject, predicate, object)。
Args:
subject (str): 主体实体。
predicate (str): 谓词/关系。
obj (str): 客体实体或属性值。
source (str): 事实来源。
"""
# 添加节点
self.graph.add_node(subject, type="entity")
self.graph.add_node(obj, type="entity_or_value") # obj可能是实体也可能是值
# 添加边(关系)
# 我们可以将谓词作为边的属性,或者作为单独的节点类型
# 为了简化,这里将谓词作为边的类型
self.graph.add_edge(subject, obj, relation=predicate, source=source)
print(f"Added fact: ({subject})-[:{predicate}]->({obj}) from source: {source}")
def update_fact_from_correction(self, extracted_knowledge: dict):
"""
根据用户纠正提取的结构化知识更新知识图谱。
Args:
extracted_knowledge (dict): 从KnowledgeExtractor获取的结构化纠正信息。
"""
entity = extracted_knowledge.get("entity")
attribute = extracted_knowledge.get("attribute")
value_before_correction = extracted_knowledge.get("value_before_correction")
value_after_correction = extracted_knowledge.get("value_after_correction")
new_correct_fact = extracted_knowledge.get("new_correct_fact") # 备用
if not entity or not attribute or not value_after_correction:
print("Insufficient structured knowledge for KG update, skipping.")
return
# 尝试查找并删除旧的错误事实
old_fact_found = False
if value_before_correction:
edges_to_remove = []
for u, v, data in self.graph.edges(data=True):
if u == entity and data.get('relation') == attribute and v == value_before_correction:
edges_to_remove.append((u, v))
print(f"Removing old incorrect fact: ({entity})-[:{attribute}]->({value_before_correction})")
old_fact_found = True
for u, v in edges_to_remove:
self.graph.remove_edge(u, v)
# 添加新的正确事实
self.add_fact(entity, attribute, value_after_correction, source="user_correction")
print(f"Knowledge Graph updated with correction for {entity}: {attribute} is now {value_after_correction}.")
if not old_fact_found and value_before_correction:
print(f"Warning: Old fact ({entity})-[:{attribute}]->({value_before_correction}) not explicitly found for removal.")
def query_fact(self, subject: str, predicate: str) -> list:
"""
查询某个实体通过某个谓词指向的所有客体。
Args:
subject (str): 主体实体。
predicate (str): 谓词/关系。
Returns:
list: 所有匹配的客体。
"""
results = []
if self.graph.has_node(subject):
for neighbor in self.graph.neighbors(subject):
# 检查边上的关系是否匹配
edge_data = self.graph.get_edge_data(subject, neighbor)
if edge_data and edge_data.get('relation') == predicate:
results.append(neighbor)
return results
def visualize_graph(self):
"""简单的图谱可视化(仅用于本地调试,不作为输出)"""
pos = nx.spring_layout(self.graph)
nx.draw(self.graph, pos, with_labels=True, node_color='skyblue', node_size=2000, font_size=10, font_weight='bold')
edge_labels = nx.get_edge_attributes(self.graph, 'relation')
nx.draw_networkx_edge_labels(self.graph, pos, edge_labels=edge_labels)
plt.show()
# 示例使用
if __name__ == "__main__":
kg_system = KnowledgeGraphSystem()
detector = CorrectionDetector()
extractor = KnowledgeExtractor()
# 添加初始知识
kg_system.add_fact("中国", "首都", "上海", source="initial_data") # 错误信息
kg_system.add_fact("Python", "发布年份", "1999", source="initial_data")
kg_system.add_fact("Guido van Rossum", "发明", "Python", source="initial_data")
# 查询初始知识
print("n--- 初始查询 ---")
print(f"中国的首都: {kg_system.query_fact('中国', '首都')}")
print(f"Python的发布年份: {kg_system.query_fact('Python', '发布年份')}")
# 用户纠正:中国的首都
agent_resp_to_correct_1 = "中国的首都是上海。"
user_correction_1 = "不对,你错了,中国的首都是北京。"
if detector.detect_correction(user_correction_1, agent_resp_to_correct_1):
print("n--- 检测到用户纠正 (中国的首都) ---")
extracted_info_1 = extractor.extract_correction_knowledge(user_correction_1, agent_resp_to_correct_1)
print(json.dumps(extracted_info_1, indent=2, ensure_ascii=False))
kg_system.update_fact_from_correction(extracted_info_1)
else:
print("n--- 未检测到用户纠正 (中国的首都) ---")
# 再次查询验证
print("n--- 纠正后查询 ---")
print(f"中国的首都: {kg_system.query_fact('中国', '首都')}")
print(f"Python的发布年份: {kg_system.query_fact('Python', '发布年份')}")
# 用户纠正:Python发布年份
agent_resp_to_correct_2 = "Python编程语言是由Guido van Rossum在1999年发明的。"
user_correction_2 = "不对,Python是Guido van Rossum在1991年左右发布的。"
if detector.detect_correction(user_correction_2, agent_resp_to_correct_2):
print("n--- 检测到用户纠正 (Python发布年份) ---")
extracted_info_2 = extractor.extract_correction_knowledge(user_correction_2, agent_resp_to_correct_2)
print(json.dumps(extracted_info_2, indent=2, ensure_ascii=False))
kg_system.update_fact_from_correction(extracted_info_2)
else:
print("n--- 未检测到用户纠正 (Python发布年份) ---")
# 再次查询验证
print("n--- 再次纠正后查询 ---")
print(f"中国的首都: {kg_system.query_fact('中国', '首都')}")
print(f"Python的发布年份: {kg_system.query_fact('Python', '发布年份')}")
# kg_system.visualize_graph() # 调用此函数可显示图谱,但文章中不显示图片
3.3 策略三:规则系统/专家系统
在某些特定且定义明确的领域,可以使用规则系统或专家系统来存储和更新知识。这种系统通过一系列“IF-THEN”规则和事实库来工作。
更新逻辑:当用户纠正时,如果纠正对应于某个规则或事实,则直接修改该规则或事实。
工作流程:
- 检测与提取:识别纠正,提取关键的事实和条件。
- 规则匹配与修改:将提取的信息与现有的规则或事实进行匹配。如果匹配成功,则修改相应的事实或规则。例如,如果 Agent 错误地回答“产品A适用于场景X”,而用户纠正为“产品A适用于场景Y”,则直接修改产品A的适用场景事实。
- Agent推理:Agent在回答时,通过推理引擎执行规则,得出结论。
优点:
- 精确控制:对于特定领域的知识,可以实现非常精确的控制和更新。
- 高可解释性:决策路径清晰,易于审计和理解。
- 确定性:在给定相同输入时,总是产生相同输出。
挑战:
- 扩展性差:随着知识的增加,规则数量会爆炸式增长,难以管理和维护。
- 适用范围有限:不适用于开放域或知识结构不明确的场景。
- 人工成本高:规则的编写和维护通常需要领域专家。
代码示例:简单的规则系统更新
这里用一个Python字典模拟事实库。
class RuleBasedKnowledgeSystem:
def __init__(self):
# 初始事实库
self.facts = {
"country_capital": {
"中国": "上海", # 错误事实
"美国": "华盛顿",
"日本": "东京"
},
"programming_language_creator": {
"Python": "Guido van Rossum"
},
"python_release_year": {
"Python": "1999" # 错误事实
}
}
print("Rule-Based Knowledge System initialized.")
def get_fact(self, category: str, key: str):
"""获取特定类别下的事实"""
return self.facts.get(category, {}).get(key)
def update_fact(self, category: str, key: str, new_value: str, source: str = "user_correction"):
"""
更新事实库中的特定事实。
Args:
category (str): 事实类别,如 "country_capital"。
key (str): 事实键,如 "中国"。
new_value (str): 新的正确值。
source (str): 更新来源。
"""
if category not in self.facts:
self.facts[category] = {}
old_value = self.facts[category].get(key)
self.facts[category][key] = new_value
print(f"Fact updated in '{category}': '{key}' from '{old_value}' to '{new_value}' by '{source}'.")
def update_knowledge_from_correction(self, extracted_knowledge: dict):
"""
根据用户纠正提取的结构化知识更新规则系统。
Args:
extracted_knowledge (dict): 从KnowledgeExtractor获取的结构化纠正信息。
"""
entity = extracted_knowledge.get("entity")
attribute = extracted_knowledge.get("attribute")
value_after_correction = extracted_knowledge.get("value_after_correction")
if not entity or not attribute or not value_after_correction:
print("Insufficient structured knowledge for Rule-Based update, skipping.")
return
# 映射到内部事实结构
if attribute == "首都":
category = "country_capital"
key = entity
elif attribute == "发布年份" and entity == "Python":
category = "python_release_year"
key = "Python"
# 可以添加更多映射规则
else:
print(f"No matching rule category for entity '{entity}' and attribute '{attribute}', skipping.")
return
self.update_fact(category, key, value_after_correction)
# 示例使用
if __name__ == "__main__":
rb_system = RuleBasedKnowledgeSystem()
detector = CorrectionDetector()
extractor = KnowledgeExtractor()
# 查询初始知识
print("n--- 初始查询 ---")
print(f"中国的首都: {rb_system.get_fact('country_capital', '中国')}")
print(f"Python的发布年份: {rb_system.get_fact('python_release_year', 'Python')}")
# 用户纠正:中国的首都
agent_resp_to_correct_1 = "中国的首都是上海。"
user_correction_1 = "不对,你错了,中国的首都是北京。"
if detector.detect_correction(user_correction_1, agent_resp_to_correct_1):
print("n--- 检测到用户纠正 (中国的首都) ---")
extracted_info_1 = extractor.extract_correction_knowledge(user_correction_1, agent_resp_to_correct_1)
print(json.dumps(extracted_info_1, indent=2, ensure_ascii=False))
rb_system.update_knowledge_from_correction(extracted_info_1)
else:
print("n--- 未检测到用户纠正 (中国的首都) ---")
# 再次查询验证
print("n--- 纠正后查询 ---")
print(f"中国的首都: {rb_system.get_fact('country_capital', '中国')}")
# 用户纠正:Python发布年份
agent_resp_to_correct_2 = "Python编程语言是由Guido van Rossum在1999年发明的。"
user_correction_2 = "不对,Python是Guido van Rossum在1991年左右发布的。"
if detector.detect_correction(user_correction_2, agent_resp_to_correct_2):
print("n--- 检测到用户纠正 (Python发布年份) ---")
extracted_info_2 = extractor.extract_correction_knowledge(user_correction_2, agent_resp_to_correct_2)
print(json.dumps(extracted_info_2, indent=2, ensure_ascii=False))
rb_system.update_knowledge_from_correction(extracted_info_2)
else:
print("n--- 未检测到用户纠正 (Python发布年份) ---")
# 再次查询验证
print("n--- 再次纠正后查询 ---")
print(f"Python的发布年份: {rb_system.get_fact('python_release_year', 'Python')}")
3.4 策略四:增量微调(Incremental Fine-tuning)/ LoRA
虽然我们强调不直接修改LLM参数,但对于长期、大规模的知识更新,特别是为了提升Agent在特定领域的一致性和风格,微调仍然是一种选择。然而,每次用户纠正都进行微调是不现实的。
更新逻辑:收集大量的用户纠正数据,经过人工审核和高质量标注后,定期(例如每周或每月)对LLM进行增量微调。可以采用Low-Rank Adaptation (LoRA) 等参数高效微调技术,只更新少量特定层,以减轻灾难性遗忘。
工作流程:
- 数据收集:所有用户纠正,连同Agent的原始错误回答和用户提供的正确信息,都被记录下来。
- 人工审核与标注:这是关键步骤。人工专家对收集到的纠正进行验证、去重、冲突解决和标准化,形成高质量的训练数据对(
{"input": "错误问题", "output": "正确答案"})。 - 模型微调:使用这些高质量数据对,对一个较小的基础模型或LLM的LoRA适配器进行微调。
- 模型部署:将微调后的模型或更新的LoRA适配器部署到生产环境。
优点:
- 深层知识整合:知识融入模型参数,可以影响模型的泛化能力和生成风格。
- 一致性:对于重复出现的错误,微调可以确保模型在每次遇到时都能给出正确答案。
- 提升特定领域性能:如果纠正集中在某个特定领域,微调可以显著提升Agent在该领域的表现。
挑战:
- 成本高昂:数据标注、训练资源和时间成本。
- 更新延迟:无法实现实时更新,通常有数天到数周的延迟。
- 灾难性遗忘风险:尽管LoRA有所缓解,但仍需精心设计训练过程。
- 数据量要求:需要足够多的高质量纠正数据才能进行有效的微调。
代码示例:数据收集器(为微调做准备)
import datetime
import json
class FineTuningDataSource:
def __init__(self, log_file="correction_log.jsonl"):
self.log_file = log_file
print(f"Fine-tuning data collector initialized, logging to {self.log_file}")
def log_correction_event(self, user_id: str, session_id: str, agent_response: str, user_correction: str, extracted_knowledge: dict):
"""
记录一个用户纠正事件,以备后续用于微调。
Args:
user_id (str): 用户的唯一标识。
session_id (str): 会话的唯一标识。
agent_response (str): Agent的错误响应。
user_correction (str): 用户的纠正输入。
extracted_knowledge (dict): 从KnowledgeExtractor获取的结构化纠正信息。
"""
event = {
"timestamp": datetime.datetime.now().isoformat(),
"user_id": user_id,
"session_id": session_id,
"agent_response": agent_response,
"user_correction": user_correction,
"extracted_knowledge": extracted_knowledge,
"status": "pending_review" # 初始状态,待人工审核
}
with open(self.log_file, 'a', encoding='utf-8') as f:
f.write(json.dumps(event, ensure_ascii=False) + 'n')
print(f"Correction event logged for user {user_id}.")
def generate_fine_tuning_data(self, reviewed_data_path: str):
"""
根据审核过的数据生成微调所需的JSONL格式数据。
这部分通常需要人工审核和清洗工具的辅助。
假设 reviewed_data_path 指向一个已经人工审核并格式化好的文件。
"""
fine_tuning_samples = []
try:
with open(reviewed_data_path, 'r', encoding='utf-8') as f:
for line in f:
data = json.loads(line)
# 假设审核后的数据格式是 {"instruction": "问题", "input": "上下文", "output": "正确答案"}
# 或直接 {"prompt": "问题", "completion": "正确答案"}
# 这里简化为直接使用 agent_response 和 user_correction 构造
if data.get("status") == "approved":
# 示例:将 Agent 之前的回答作为 Prompt,用户的纠正作为 Completion
# 实际情况中,Prompt 可能更复杂,包含用户原始问题和 Agent 错误回答
prompt_text = f"Agent说: '{data['agent_response']}' 用户纠正说: "
completion_text = data['user_correction'] # 或者 extracted_knowledge["new_correct_fact"]
fine_tuning_samples.append({
"prompt": prompt_text,
"completion": completion_text
})
print(f"Generated {len(fine_tuning_samples)} fine-tuning samples.")
return fine_tuning_samples
except FileNotFoundError:
print(f"Reviewed data file not found at {reviewed_data_path}")
return []
except Exception as e:
print(f"Error generating fine-tuning data: {e}")
return []
# 示例使用
if __name__ == "__main__":
ft_data_source = FineTuningDataSource()
detector = CorrectionDetector()
extractor = KnowledgeExtractor()
# 模拟一次用户纠正流程
agent_resp_example = "地球的卫星是火星。"
user_input_example = "不对,地球的卫星是月亮。"
user_id_example = "user_123"
session_id_example = "sess_abc"
if detector.detect_correction(user_input_example, agent_resp_example):
extracted_info_example = extractor.extract_correction_knowledge(user_input_example, agent_resp_example)
ft_data_source.log_correction_event(
user_id=user_id_example,
session_id=session_id_example,
agent_response=agent_resp_example,
user_correction=user_input_example,
extracted_knowledge=extracted_info_example
)
# 模拟人工审核后的数据文件
# 实际中,这个文件是通过人工工具生成的
mock_reviewed_data = [
{"timestamp": "...", "user_id": "user_123", "agent_response": "地球的卫星是火星。", "user_correction": "不对,地球的卫星是月亮。", "extracted_knowledge": {...}, "status": "approved"},
{"timestamp": "...", "user_id": "user_456", "agent_response": "Python发布于1999年。", "user_correction": "实际上Python发布于1991年。", "extracted_knowledge": {...}, "status": "approved"},
{"timestamp": "...", "user_id": "user_789", "agent_response": "中国的首都是上海。", "user_correction": "北京是中国的首都。", "extracted_knowledge": {...}, "status": "approved"},
{"timestamp": "...", "user_id": "user_101", "agent_response": "随便说点什么。", "user_correction": "没啥,继续。", "extracted_knowledge": {}, "status": "rejected"} # 非纠正或低质量纠正
]
# 将模拟数据写入一个临时文件,以便 generate_fine_tuning_data 读取
mock_reviewed_file = "mock_reviewed_corrections.jsonl"
with open(mock_reviewed_file, 'w', encoding='utf-8') as f:
for item in mock_reviewed_data:
f.write(json.dumps(item, ensure_ascii=False) + 'n')
fine_tuning_data = ft_data_source.generate_fine_tuning_data(mock_reviewed_file)
print("n--- 生成的微调数据样本 ---")
for sample in fine_tuning_data[:2]: # 打印前两个样本
print(sample)
# 清理临时文件
os.remove(mock_reviewed_file)
3.5 策略五:混合式方法(Hybrid Approach)
在实际应用中,单一的策略往往难以满足所有需求。最有效的方法通常是结合多种策略,取长补短。
一个典型的混合架构可能包括:
- RAG知识库:作为Agent的核心长期记忆,处理大多数通用事实和实时更新。
- 知识图谱:用于存储结构化、关系复杂的领域知识,并支持推理。
- 规则系统:用于处理少量关键、确定性强的业务规则或安全策略。
- 微调数据收集与周期性微调:作为长期优化机制,提升Agent在特定领域的一致性和性能。
工作流程概览:
- 用户纠正 -> 纠正检测 -> 知识提取 (如前所述)。
- 知识分发决策:根据提取出的知识类型和重要性,决定将其分发到哪个或哪些知识库。
- 通用事实纠正(如某个国家的首都):优先更新RAG知识库。
- 结构化、关系性强的纠正(如实体属性、实体间关系):更新知识图谱。
- 关键业务规则或安全策略纠正:更新规则系统(如果适用)。
- 所有纠正:都记录到微调数据收集器,待人工审核后用于周期性微调。
- Agent查询:Agent在回答用户问题时,会同时查询RAG、知识图谱和规则系统,并综合它们的信息来生成最终答案。通常RAG提供上下文,知识图谱提供结构化事实,规则系统提供明确判断。
知识更新逻辑的决策流程:
| 知识类型 | 首选更新策略 | 辅助策略 | 优点 | 缺点 |
|---|---|---|---|---|
| 通用事实性纠正 | RAG | 微调数据收集 | 实时、成本低、可扩展 | 冲突解决需额外机制,知识结构化程度低 |
| 结构化、关系性纠正 | 知识图谱 | RAG,微调数据收集 | 结构清晰、支持推理、易于冲突管理 | 抽取复杂、模式演进、维护成本高 |
| 关键业务规则 | 规则系统 | 微调数据收集 | 精确控制、高可解释性、确定性 | 扩展性差、适用范围窄、人工维护 |
| 提升泛化与风格 | 周期性微调 | – | 深度学习、影响模型行为 | 成本高、更新延迟、数据量要求、灾难性遗忘 |
第四章:实现细节与关键考虑
在构建这样的系统时,除了核心的更新策略,还需要关注一些重要的工程和产品细节。
4.1 数据验证与冲突解决
- 人工审核(Human-in-the-Loop, HITL):对于重要的、高风险的知识更新,引入人工审核是必不可少的。审核人员可以验证纠正的准确性、消除冲突、确保数据质量和安全性。
- 置信度评分:为每个知识点赋予一个置信度分数。用户纠正的知识可以有一个较高的初始置信度,或者根据纠正用户的权限/历史表现来调整。
- 来源权威性:在多个知识来源(如初始知识库、RAG、知识图谱、不同用户纠正)之间发生冲突时,定义一个权威性层级来决定采纳哪个信息。
- 版本控制:对知识库中的关键知识点进行版本管理,可以追踪知识的演变历史,并在必要时进行回滚。
4.2 知识的“遗忘”机制
仅仅添加新知识是不够的,还需要能够有效地“遗忘”或“抑制”旧的、错误的知识。
- RAG:可以通过在检索时过滤掉带有“已过时”或“错误”标签的文档,或者让新知识的向量与旧知识的向量在语义上产生“排斥”效应(更复杂)。最直接的方式是,如果能通过ID定位旧文档,直接删除。
- 知识图谱:可以直接删除旧的边或节点,或者将旧的知识标记为“已废弃”,并添加新的“替代关系”。
- 规则系统:直接修改或删除旧的规则/事实。
- 微调:通过在训练数据中加入“反例”或用最新正确数据覆盖旧数据,模型会逐渐“忘记”旧的错误信息。
4.3 及时性与效率
- 实时更新:RAG和知识图谱通常可以实现近实时的更新,一旦数据写入数据库,即可供Agent查询。
- 异步处理:知识提取和更新过程可以在后台异步进行,不阻塞用户交互。
- 缓存策略:对于频繁查询的知识,可以引入缓存机制,提高响应速度。
4.4 安全与隐私
- 脱敏处理:确保用户纠正中不包含敏感个人信息。
- 访问控制:对知识库的读写权限进行严格控制。
- 审计日志:记录所有知识更新操作,包括谁在何时更新了什么。
4.5 用户体验与反馈循环
- 清晰的反馈界面:设计简洁的用户界面,让用户能够方便地提交纠正和反馈。
- Agent的确认:当Agent接受并处理了用户的纠正后,可以给用户一个明确的反馈,例如“感谢您的纠正,我已经更新了关于XX的知识。”这能增强用户的参与感和信任。
第五章:一个集成系统设计示例
为了更好地理解上述策略如何协同工作,我们来构想一个简化的集成系统设计。
# main_agent_system.py
import datetime
from typing import Optional
# 导入之前定义的组件
from correction_detection import CorrectionDetector
from knowledge_extraction import KnowledgeExtractor
from dynamic_rag import DynamicRAGKnowledgeBase
from knowledge_graph import KnowledgeGraphSystem
from rule_based_system import RuleBasedKnowledgeSystem
from fine_tuning_data_collector import FineTuningDataSource
class IntegratedAgentKnowledgeSystem:
def __init__(self, rag_db_path="./chroma_db", ft_log_path="correction_log.jsonl"):
self.detector = CorrectionDetector()
self.extractor = KnowledgeExtractor()
self.rag_kb = DynamicRAGKnowledgeBase(persist_directory=rag_db_path)
self.kg_system = KnowledgeGraphSystem()
self.rb_system = RuleBasedKnowledgeSystem() # 规则系统
self.ft_data_source = FineTuningDataSource(log_file=ft_log_path)
self.conversation_history = [] # 存储对话历史,便于检测纠正
print("n--- Integrated Agent Knowledge System Initialized ---")
# 初始知识加载(可以从文件或数据库加载)
self._load_initial_knowledge()
def _load_initial_knowledge(self):
# 示例:加载一些初始知识到各个系统
self.rag_kb.add_initial_knowledge([
"Python编程语言是由Guido van Rossum在1999年发明的。",
"地球围绕太阳转。",
"全球最高建筑是台北101。",
"中国的首都是上海。" # 初始错误信息
])
self.kg_system.add_fact("中国", "首都", "上海", source="initial_data") # 初始错误信息
self.kg_system.add_fact("Python", "发布年份", "1999", source="initial_data") # 初始错误信息
self.kg_system.add_fact("Guido van Rossum", "发明", "Python", source="initial_data")
# 规则系统已在初始化时包含初始事实
def process_user_input(self, user_id: str, session_id: str, user_input: str) -> str:
"""
处理用户输入,包括生成Agent响应和处理可能的纠正。
"""
agent_response = self._generate_agent_response(user_input)
# 将当前对话加入历史
self.conversation_history.append({"role": "user", "content": user_input, "timestamp": datetime.datetime.now().isoformat()})
self.conversation_history.append({"role": "agent", "content": agent_response, "timestamp": datetime.datetime.now().isoformat()})
# 检查是否是用户纠正
if len(self.conversation_history) >= 2:
# 取最近的Agent响应和用户输入进行检测
last_agent_resp = self.conversation_history[-2]["content"]
if self.detector.detect_correction(user_input, last_agent_resp):
print(f"n--- Detected user correction for user {user_id} ---")
extracted_info = self.extractor.extract_correction_knowledge(user_input, last_agent_resp)
extracted_info["timestamp"] = datetime.datetime.now().isoformat() # 记录时间
# 异步更新知识库(实际中可能放入消息队列)
self._async_update_knowledge_bases(user_id, session_id, last_agent_resp, user_input, extracted_info)
agent_response += "n(感谢您的纠正,我已记录并会更新我的知识库。)" # 给用户反馈
return agent_response
def _generate_agent_response(self, user_query: str) -> str:
"""
Agent根据所有知识库综合生成响应。
这是一个简化的逻辑,实际中会更复杂,可能涉及LLM决策、工具调用等。
"""
# 1. 优先查询规则系统(用于关键事实或业务规则)
if "首都" in user_query and "中国" in user_query:
capital = self.rb_system.get_fact("country_capital", "中国")
if capital:
return f"根据我的规则系统,中国的首都是{capital}。"
if "Python" in user_query and ("发布年份" in user_query or "发明时间" in user_query):
year = self.rb_system.get_fact("python_release_year", "Python")
if year:
return f"Python的发布年份是{year}。"
# 2. 如果规则系统未命中,查询知识图谱(用于结构化事实和关系)
# 简化:如果能从KG中获取到明确的答案
kg_capital = self.kg_system.query_fact("中国", "首都")
if kg_capital:
return f"根据知识图谱,中国的首都是{kg_capital[0]}。"
kg_python_year = self.kg_system.query_fact("Python", "发布年份")
if kg_python_year:
return f"根据知识图谱,Python的发布年份是{kg_python_year[0]}。"
# 3. 最后,使用RAG系统结合LLM生成答案(处理通用问题和更复杂的查询)
# RAG系统会检索所有相关文档,包括用户纠正的文档,然后LLM生成答案
return self.rag_kb.query_knowledge(user_query)
def _async_update_knowledge_bases(self, user_id: str, session_id: str, agent_resp: str, user_input: str, extracted_info: dict):
"""
异步更新所有相关的知识库。
"""
print("开始异步更新知识库...")
# 更新RAG知识库
self.rag_kb.update_knowledge_from_correction(extracted_info)
# 更新知识图谱
self.kg_system.update_fact_from_correction(extracted_info)
# 更新规则系统(如果适用)
self.rb_system.update_knowledge_from_correction(extracted_info)
# 记录到微调数据源
self.ft_data_source.log_correction_event(
user_id=user_id,
session_id=session_id,
agent_response=agent_resp,
user_correction=user_input,
extracted_knowledge=extracted_info
)
print("知识库更新任务已提交。")
# 运行示例
if __name__ == "__main__":
# 清理旧的DB和日志文件
if os.path.exists("./chroma_db"):
import shutil
shutil.rmtree("./chroma_db")
if os.path.exists("correction_log.jsonl"):
os.remove("correction_log.jsonl")
print("Cleaned up previous DB and log files.")
agent_system = IntegratedAgentKnowledgeSystem()
user_id_1 = "test_user_001"
session_id_1 = "test_session_001"
print("n--- 模拟对话 1: 首次提问 (Agent可能出错) ---")
response_1 = agent_system.process_user_input(user_id_1, session_id_1, "中国的首都是哪里?")
print(f"Agent: {response_1}")
print("n--- 模拟对话 2: 用户纠正 ---")
response_2 = agent_system.process_user_input(user_id_1, session_id_1, "不对,你错了,中国的首都是北京。")
print(f"Agent: {response_2}")
print("n--- 模拟对话 3: 再次提问 (Agent应已学习) ---")
response_3 = agent_system.process_user_input(user_id_1, session_id_1, "请告诉我中国的首都。")
print(f"Agent: {response_3}")
print("n--- 模拟对话 4: 提问另一个可能出错的问题 ---")
response_4 = agent_system.process_user_input(user_id_1, session_id_1, "Python是什么时候发布的?")
print(f"Agent: {response_4}")
print("n--- 模拟对话 5: 用户纠正 Python 发布年份 ---")
response_5 = agent_system.process_user_input(user_id_1, session_id_1, "不是1999年,Python是1991年发布的。")
print(f"Agent: {response_5}")
print("n--- 模拟对话 6: 再次提问 Python 发布年份 ---")
response_6 = agent_system.process_user_input(user_id_1, session_id_1, "Python的发布年份是?")
print(f"Agent: {response_6}")
print("n--- 查看微调数据日志 ---")
with open("correction_log.jsonl", 'r', encoding='utf-8') as f:
for line in f:
print(json.loads(line))
系统架构描述:
- Agent Core: 接收用户输入,维护对话历史。
- Correction Detector: 识别用户输入是否为纠正。
- Knowledge Extractor: 从纠正中提取结构化知识(实体、属性、新旧值)。
- Knowledge Update Manager (异步): 接收提取的知识,并根据预设逻辑将其分发到不同的长期记忆组件:
- Dynamic RAG Knowledge Base: 实时更新向量数据库中的文档,处理通用事实纠正。
- Knowledge Graph System: 更新知识图谱中的实体和关系,处理结构化事实纠正。
- Rule-Based Knowledge System: 更新精确的业务规则或关键事实。
- Fine-Tuning Data Source: 记录所有纠正,作为未来模型微调的数据集来源。
- Agent Response Generator: 在生成响应时,Agent会层级地查询这些知识库:
- 首先查询规则系统,如果命中则直接使用规则提供的答案(优先级最高)。
- 其次查询知识图谱,获取结构化、关系性强的知识。
- 最后,将前两步可能获得的信息和用户查询一起,通过RAG系统(包括LLM)生成最终答案。通过RAG,即使用户纠正的信息只是一个文本块,也能够被检索到并影响LLM的输出。
这种混合方法兼顾了实时性、准确性、结构化和长期模型优化的需求,是构建健壮、自适应Agent的有效途径。
构建一个能够从用户纠正中永久学习的智能Agent,是提升其智能水平和用户体验的关键。这要求我们超越单一模型范式,设计一个多层次、多策略的知识管理系统。从实时RAG更新到结构化的知识图谱,再到周期性的模型微调,每种方法都在Agent的“学习”过程中扮演着不可或缺的角色。通过精巧的工程设计和持续的数据迭代,我们能够逐步打造出更加智能、更加可靠的AI助手。