从零开发一个Agent

#从零开发一个Agent [TOC]

AgentRAG概述

检索向量后,判断文本块是否包含问题答案 如包含则进行回答 如不包含则重新进入向量检索,匹配+1块 最大循环次数15

核心代码

从向量数据库匹配文档,并让大模型反思 每次循环增加一个文档块,直到能回答问题 文档块数量超过15个仍无法回答问题则放弃

代码示例

首先安装相关依赖

bash 复制代码
! pip install -qU langchain-openai langchain langchain_community langchainhub
! pip install chromadb==0.5.3

导入相关包,把上次传统的RAG代码给粘贴过来。

python 复制代码
from langchain import hub as langchain_hub
from langchain.schema import StrOutputParser
from langchain_openai import ChatOpenAI
from langchain.schema.runnable import RunnablePassthrough
from langchain.text_splitter import MarkdownHeaderTextSplitter
from langchain_openai import OpenAIEmbeddings
import os
#from langchain_chroma import Chroma
from langchain_community.vectorstores.chroma import Chroma
from langchain_core.prompts import PromptTemplate
from string import Template
import uuid
python 复制代码
# 读取 ./data/data.md 文件作为运维知识库
file_path = os.path.join('data', 'data.md')
with open(file_path, 'r', encoding='utf-8') as file:
    docs_string = file.read()

# Split the document into chunks base on markdown headers.
headers_to_split_on = [
    ("#", "Header 1"),
    ("##", "Header 2"),
    ("###", "Header 3"),
]
text_splitter = MarkdownHeaderTextSplitter(headers_to_split_on=headers_to_split_on)
splits = text_splitter.split_text(docs_string)
print("Length of splits: " + str(len(splits)))
print(splits)

这里输出了一下分片,接下来我们将运维知识库的每一块文本向量化(Embedding)

python 复制代码
# 保存到随机目录里
random_directory = "./" + str(uuid.uuid4())
embedding = OpenAIEmbeddings(
    model="text-embedding-3-small",
    openai_api_key="你的api key"
)
vectorstore = Chroma.from_documents(documents=splits, embedding=embedding, persist_directory=random_directory)
# vectorstore.persist()

接着我们使用代码实现了一个传统的检索增强生成(RAG)流程,其中通过向量数据库检索相关文档、使用模板生成提示、并利用 GPT-4 模型生成答案,最后输出简洁回答。

python 复制代码
# 传统 RAG 流程
retriever = vectorstore.as_retriever()
# 提示语模板
template = """使用以下上下文来回答最后的问题。
如果你不知道答案,就说不知道,不要试图编造答案。
最多使用三句话,并尽可能简洁地回答。
在答案的最后一定要说"谢谢询问!"。

{context}

Question: {question}

Helpful Answer:"""
custom_rag_prompt = PromptTemplate.from_template(template)

def format_docs(docs):
    print("匹配到的运维知识库片段:\n", "\n\n".join(doc.page_content for doc in docs))
    return "\n\n".join(doc.page_content for doc in docs)

llm = ChatOpenAI(model="gpt-4o",api_key="你的key")
rag_chain = (
    {"context": retriever | format_docs, "question": RunnablePassthrough()}
    | custom_rag_prompt
    | llm
    | StrOutputParser()
)


# 传统 RAG 无法回答的问题
res = rag_chain.invoke("谁管的系统最多?")
print("\n\nLLM 回答:", res)

接着我们实现了一个基于 Agent RAG 流程的问答系统,通过逐步增加检索的文档数量来判断是否足够回答问题,最终生成答案。如果上下文不足以回答问题,系统会增加更多文档并重新检查,直到能够回答问题为止。

python 复制代码
# Agent RAG 流程

# 文本相似性检索
def search_docs(query, k=1):
    results = vectorstore.similarity_search_with_score(
        query,
        k=k,
    )
    return "\n\n".join(doc.page_content for doc, score in results)

user_query = "谁管的系统最多?"

check_can_answer_system_prompt = """
根据上下文识别是否能够回答问题,如果不能,则返回 JSON 字符串 "{"can_answer": false}",如果可以则返回 "{"can_answer": true}"。
上下文:\n $context
问题:$question

"""
llm = ChatOpenAI(model="gpt-4o",api_key="你的API Key")
k = 1
docs = ""
while True:
    # 通过检索找到相关文档,每次循环增加一个检索文档数量,最大 15 个文档块
    print("第", k, "次检索")
    if k > 15:
        break
    docs = search_docs(user_query, k)
    print("匹配到的文档块: ", docs)
    template = Template(check_can_answer_system_prompt)
    filled_prompt = template.substitute(question=user_query, context=docs)
    # 检查上下文是否足够回答问题
    messages = [
        (
            "system",
            filled_prompt,
        ),
        ("human", "开始检查上下文是否足够回答问题。"),
    ]
    llm_message = llm.invoke(messages)
    content = llm_message.content
    print("\nLLM Res: ", content, "\n")
    if content == '{"can_answer": true}':
        break
    else:
        k += 1

print("匹配到能够回答问题的知识库,开始进行回答\n")

# 最终推理
final_system_prompt = """
您是问答任务的助手,使用检索到的上下文来回答用户提出的问题。如果你不知道答案,就说不知道。最多使用三句话并保持答案简洁。
"""

final_messages = [
    (
        "system",
        final_system_prompt,
    ),
    ("human", "上下文:\n"+ docs +"\n问题:" + user_query),
]
llm_message = llm.invoke(final_messages)
content = llm_message.content
print("\nLLM Final Res: ", content, "\n")

在经历第9次后我们发现它能够回答我们的问题了。

改进方向

判断文本块大小,并使用切片策略,避免上下文超限 改进向量搜索策略,例如通过增加offset参数过滤已验证无法回答问题的文本块 判断能否回答问题环节使用JSONMode获得更稳定的结构化输出

相关推荐
闲不住的李先森2 小时前
AI 应用演进:从基础调用到自主智能体
人工智能·llm·aigc
新智元3 小时前
刚刚,英伟达新模型上线!4B 推理狂飙 53 倍,全新注意力架构超越 Mamba 2
人工智能·openai
新智元3 小时前
北大数学家终结 50 年猜想!一只蝴蝶翅膀,竟难倒菲尔兹奖得主
人工智能·openai
机器之心3 小时前
热议!DeepSeek V3.1惊现神秘「极」字Bug,模型故障了?
人工智能·openai
DisonTangor3 小时前
【字节拥抱开源】字节豆包团队开源豆包OSS大模型
人工智能·开源·aigc
得物技术4 小时前
得物新商品审核链路建设分享
后端·架构·aigc
尘叶心简4 小时前
TranslationAgent源码和架构分析
aigc·openai
深度学习机器8 小时前
aisuite:统一的大模型SDK,简化LLM开发流程
chatgpt·llm·openai
Mintopia9 小时前
📡 大语言模型如何重塑 WebAI 的交互逻辑?
前端·javascript·aigc