目录
第三篇讲过工具调用和 Agent,第四篇到第七篇讲了 RAG。
这一篇把两条线合在一起:给 Agent 一组博客知识库工具,让它自己决定什么时候检索文章、什么时候读取来源、什么时候直接回答。
本文对应代码位于 code/langchain-demo/chapter08。
本篇目标
第八篇要实现的是一个“博客问答 Agent”。
它有三个工具:
search_blog_posts:根据问题检索博客文章。list_blog_categories:列出博客分类。read_blog_source:读取某篇博客正文开头。
这样用户问博客内容时,Agent 不需要凭空回答,而是先调用工具检索资料。
代码结构
第八篇代码结构如下:
chapter08/
__init__.py
config.py
llm.py
rag_utils.py
blog_tools.py
blog_qa_agent.py
其中:
rag_utils.py:复用前面 RAG 的读取、切分、检索逻辑。blog_tools.py:把检索能力包装成 LangChain 工具。blog_qa_agent.py:创建 Agent 并运行问题。
定义博客工具
工具代码放在 blog_tools.py。
最重要的是 search_blog_posts:
@tool
def search_blog_posts(question: str) -> str:
"""根据用户问题检索本地博客文章,返回相关片段和来源。"""
documents = load_documents()
chunks = split_documents(documents)
relevant_chunks = retrieve(question, chunks, top_k=4)
if not relevant_chunks:
return "没有检索到相关博客片段。"
return (
f"命中关键词:{keyword_report(question, relevant_chunks)}\n\n"
f"{format_context(relevant_chunks)}"
)
这个工具内部还是做 RAG 检索,但对 Agent 来说,它只是一个可调用工具。
也就是说,模型看到的是:
我有一个 search_blog_posts 工具,可以根据问题检索本地博客文章。
至于工具里面是关键词检索、向量检索,还是调用数据库,Agent 并不关心。
这就是工具抽象的好处。
读取来源工具
除了搜索,还提供了一个读取文章正文的工具:
@tool
def read_blog_source(source: str) -> str:
"""读取某篇博客 Markdown 的正文开头,source 必须来自检索结果。"""
这里加了路径限制:
path = (REPO_ROOT / source).resolve()
blog_root = BLOG_POST_ROOT.resolve()
if not path.is_file() or blog_root not in path.parents:
return "只能读取 blog/content/post 目录下的文章。"
这个限制很重要。
工具一旦给 Agent 使用,就要考虑边界,不能让它随便读任意路径。
创建 Agent
Agent 创建代码和第三篇类似:
agent = create_agent(
model=create_chat_model(temperature=0),
tools=TOOLS,
system_prompt=(
"你是一个本地博客问答 Agent。"
"回答博客内容相关问题时,必须先调用 search_blog_posts。"
"如果用户询问分类,可以调用 list_blog_categories。"
"最终答案要用中文,并列出参考来源。"
),
)
这里的 system prompt 明确要求:回答博客内容相关问题时,必须先调用 search_blog_posts。
否则模型可能会直接根据已有知识回答,这就失去了 RAG 的意义。
运行 demo
在 code/langchain-demo 目录下运行:
uv run python -m chapter08.blog_qa_agent "Python 函数式编程讲了什么?请给出参考文章。"
输出里会打印消息过程:
Messages:
- human: Python 函数式编程讲了什么?请给出参考文章。
- ai:
tool_calls: [...]
- tool: 命中关键词:...
- ai: ...
这里可以看到 Agent 的执行过程:
- 用户提出问题。
- 模型决定调用
search_blog_posts。 - 工具返回检索片段。
- 模型基于工具结果生成最终回答。
这比直接看最终答案更有价值,因为可以确认模型是否真的用了工具。
Agent 和 RAG 的关系
RAG 是一种给模型补充上下文的方法。
Agent 是一种让模型根据目标自主选择工具的执行方式。
两者可以组合:
Agent 负责决策
RAG 工具负责查资料
LLM 负责总结回答
在简单问答里,直接写 RAG chain 就够了。
但如果用户问题类型多了,比如有时候查分类、有时候读文章、有时候做总结,就可以把这些能力做成工具,让 Agent 来调度。
小结
这一篇把第三篇的 Agent 和前几篇的 RAG 合在了一起。
到这里,我们已经不是只写一条固定链路,而是让模型通过工具访问本地博客知识库。
下一篇继续往前补一个常见能力:多轮对话。用户不会每次都把问题说完整,所以需要让系统记住上一轮说了什么。
参考
code/langchain-demo/chapter08- LangChain
@tool - LangChain
create_agent - RAG 工具化
原创文章,转载请注明来源: LangChain 入门学习第八篇-带工具的博客问答 Agent
相关文章
- LangChain 入门学习第七篇-RAG 检索质量优化
- LangChain 入门学习第六篇-持久化索引和本地知识库问答
- LangChain 入门学习第五篇-使用本地 Embeddings 实现向量检索 RAG
- LangChain 入门学习第四篇-基于本地 Markdown 的 RAG 问答
- LangChain 入门学习第三篇-工具调用和 Agent
- LangChain 入门学习第二篇-PromptTemplate 和结构化输出
- LangChain 入门学习第一篇-环境准备和模型调用
- 使用 Cursor 进行 AI 编程的年度总结
- 真的,AI 可能就是新时代的信息差
- 充值 Cursor 之后,工作有了哪些变化?🤔