微信扫码
添加专属顾问
我要投稿
打造高效易用的SQL查询Agent:基于LangChain生态实现自然语言交互与可视化调试。 核心内容: 1. 项目目标:构建支持自然语言查询SQLite的Agent,集成Web UI与调试工具 2. 技术架构:LangGraph状态机管理多步流程,LangSmith实现可视化追踪 3. 开发优势:框架原生能力大幅降低代码量,标准化封装提升稳定性
在之前的文章中,我介绍了一种基于 LangChain Core实现 Agent 的开发思路。这个 Agent 能实现基本的自然语言查询能力,但只支持 CLI 运行,用户界面不够友好。
此外,当时使用了 LangChain 自带的 PromptTemplate
来手动实现 ReAct 流程,整个逻辑偏重流程控制,代码量较多,调试体验也不理想。为解决 LLM 和 Agent 的交互调试问题,我甚至还手动实现了一套日志系统。
后来在调研 Google ADK 框架时,我发现它的生态非常完善,像提示词管理、ReAct 流程、日志追踪等功能都有原生支持,基本开箱即用,开发效率大幅提升。基于 ADK 框架开发的 Agent 代码量也很低,代码结构非常简洁,开发人员只需要编写最关键的应用层核心逻辑即可。
受到 ADK 架构的启发,我开始思考能否在 LangChain 生态中复现类似的工程结构。经过一番研究,我发现其实也可以通过 LangChain 的多个组件组合出一套体验不错的方案。现分享如下。
本项目旨在实现一个支持 自然语言查询 SQLite 数据库的 LLM Agent,并具备以下几个特性:
1. 支持自然语言对话查询(输入 SQL 意图,输出结果)
2. 提供面向 C 端用户的 Web UI(基于 Streamlit)
3. 提供开发人员可调试的 Web UI(基于 LangSmith)
4. 尽量使用框架原生能力,降低代码量
为达成上述目标,我选用了 LangChain 生态中的以下关键组件:
LangChain Core 提供了整个 Agent 运行的核心框架。上篇文章中,Agent实现、Tool筛选、SQLite访问、LLM交互等基础功能均基于该核心包实现。此次实践中,我们仅保留SQLite和LLM原生包的使用,其余功能交由 LangGraph实现。
在本项目中,我使用 LangGraph 管理 Agent 的执行节点,明确分离“意图解析、工具选择、结果生成”等步骤。
同时,由于 LangGraph 自带 ReAct 框架,也省去了我们通过 PromptTemplate 实现 ReAct 流程的代码。不但降低了代码量,更通过标准化的封装降低了代码出错的概率。
3. LangSmith(必选)
LangSmith 提供了一套非常强大的 LLM 调试与可视化工具,支持:
1) 对 Agent 执行过程的可视化追踪
2) 对提示词和模型响应的逐步分析
3) 快速定位失败样本和异常行为
开发过程中,我们通过 LangSmith 实现了 Agent 开发的可追溯性和交互透明性,大幅提升了调试效率。
但必须要注意的是,区别于 ADK 自带的 Web 调试功能,LangSmith 是一个部署在云端的Web环境,对于使用有一定的免费额度,超过额度需要收费。本质上,其还是个商用的收费工具。
相关LangChain组件的生态
LangSmith收费信息:https://www.langchain.com/pricing
Streamlit 是一个轻量级 Web UI 框架,适合快速构建原型。我用它来为 Agent 构建面向 C 端用户的交互界面,实现文本输入、流式输出、执行日志展示等功能。
相比传统的前端组合,Streamlit 更快更轻,能快速构建 MVP。
对于多工具 Agent,可以通过提示词约束或函数方式对工具进行预筛选,减少模型决策负担,提高执行准确率。
在实际的使用中,我发现筛选后的 Tool 数量在小于4个时候会影响 LLM 的判断,降低 LLM 最终选择工具的效果。甚至极端情况下,筛选后的 Tool 不能承担起执行任务的能力,造成 LLM 陷入无限循环选择的情况。即使小于4个的 Tool能够被 LLM正确识别,可能多 Agent 实现才是一个更合理的设计思路。
当在 Tool 总体数量不多的情况下,该预选机制的效果比较有限。比如 Tool 总量为6个,假设预筛选保留4个结果传给 LLM,那么其实也就节省了2个 Tool 的描述Token,但其引入的embedding会增加代码复杂度。
基于以上分析,如果没有很强烈的理由,我个人不是很推荐使用工具预筛选机制。
langchain_agent/├── __init__.py├── agents/│ ├── __init__.py│ └── sql_agent.py # SQL Agent 实现├── config/│ ├── __init__.py│ ├── settings.py # 应用设置│ ├── llm_config.py # LLM 配置│ └── database_config.py # 数据库配置└── utils/ ├── __init__.py ├── embeddings.py # 嵌入模型工具(非必须) └── retrieval.py # 工具检索功能(非必须)
# Core dependencies
langchain>=0.1.0
langchain-community>=0.0.20
langchain-core>=0.1.0
langchain-DeepSeek>=0.1.0
langgraph>=0.1.0
python-dotenv>=1.0.0
faiss-cpu>=1.7.0
# Streamlit support
streamlit>=1.28.0
# LangChain Agent 环境配置
# ===== LLM 配置 =====
DEEPSEEK_API_KEY=
# LLM模型配置
LLM_MODEL=deepseek-chat
LLM_TEMPERATURE=0.0
LLM_MAX_TOKENS=4000
LLM_TIMEOUT=60
# ===== 数据库配置 =====
# 数据库文件路径
DATABASE_PATH=
DATABASE_TYPE=sqlite
# LangChain监控配置# LangSmith集成 - 零配置监控LANGCHAIN_TRACING_V2=trueLANGCHAIN_API_KEY=LANGCHAIN_PROJECT=sqlite-agentLANGCHAIN_ENDPOINT=https://api.smith.langchain.com
from langchain_community.utilities.sql_database import SQLDatabase
from langchain_agent.config.settings import get_settings
if config is None:
settings = get_settings()
config = DatabaseConfig(
database_path=settings.database_path,
database_type=settings.database_type
)
config.validate()
try:
db = SQLDatabase.from_uri(
config.database_uri,
**config.connection_params
)
return db
from langchain_deepseek import ChatDeepSeek
from langchain_agent.config.settings import get_settings
if config is None:
settings = get_settings()
config = LLMConfig(
api_key=settings.deepseek_api_key,
model=settings.llm_model,
temperature=settings.llm_temperature,
max_tokens=settings.llm_max_tokens,
timeout=settings.llm_timeout
)
try:
llm = ChatDeepSeek(
api_key=config.api_key,
model=config.model,
temperature=config.temperature,
max_tokens=config.max_tokens,
timeout=config.timeout,
**config.extra_params
)
return llm
# 创建数据库连接 from langchain_agent.config.database_config import DatabaseConfig db_config = DatabaseConfig( database_path=self.database_path, database_type=self.settings.database_type ) self.db = create_database_connection(db_config)
# 创建LLM self.llm = llm if llm else create_llm()
from langchain_community.agent_toolkits import SQLDatabaseToolkit # 创建SQL工具包 self.toolkit = SQLDatabaseToolkit(db=self.db, llm=self.llm) self.all_tools = self.toolkit.get_tools()
self.system_message = self._create_system_message()
def _create_system_message(self) -> str:
"""创建系统提示"""
return """You are an agent designed to interact with a SQL database.
Given an input question, create a syntactically correct {dialect} query to run, then look at the results of the query and return the answer.
Unless the user specifies a specific number of examples they wish to obtain, always limit your query to at most {top_k} results.
You can order the results by a relevant column to return the most interesting examples in the database.
Never query for all the columns from a specific table, only ask for the relevant columns given the question.
You MUST double check your query before executing it. If you get an error while executing a query, rewrite the query and try again.
DO NOT make any DML statements (INSERT, UPDATE, DELETE, DROP etc.) to the database.
To start you should ALWAYS look at the tables in the database to see what you can query. Do NOT skip this step.
Then you should query the schema of the most relevant tables.""".format(
dialect=self.db.dialect,
top_k=10
)
try: self.agent = create_react_agent( self.llm, self.current_tools, prompt=self.system_message )
from langgraph.prebuilt import create_react_agent
def create_sql_agent(
database_path: Optional[str] = None,
llm: Optional[BaseLanguageModel] = None
) -> SQLAgent:
return SQLAgent(
database_path=database_path,
llm=llm
)
from langchain_core.messages import HumanMessage
def query(self, question: str, user_id: Optional[str] = None) -> str:
"""
执行SQL查询
Args:
question: 用户问题
user_id: 用户ID(用于监控)
Returns:
str: 查询结果
"""
try:
config = self._prepare_query_config(question, user_id, "single")
result = self.agent.invoke({
"messages": [HumanMessage(content=question)]
}, config=config)
# 提取最后一条AI消息作为答案
messages = result.get("messages", [])
for message in reversed(messages):
if hasattr(message, 'type') and message.type == 'ai':
return message.content
return "未能获得查询结果"
except Exception as e:
return f"查询执行失败: {str(e)}"
import streamlit as st
# 用户输入
if prompt := st.chat_input("输入你的问题..."):
# 添加用户消息
st.session_state.messages.append({"role": "user", "content": prompt})
with st.chat_message("user"):
st.markdown(prompt)
# 生成回复
with st.chat_message("assistant"):
with st.spinner("正在思考..."):
try:
response = st.session_state.agent.query(prompt)
st.markdown(response)
st.session_state.messages.append({"role": "assistant", "content": response})
except Exception as e:
error_msg = f"查询出错: {str(e)}"
st.error(error_msg)
st.session_state.messages.append({"role": "assistant", "content": error_msg})
streamlit run app.py
通过将 LangChain 的多组件组合使用,我们可以构建出一套完整的、带 Web UI 的可交互 Agent 系统。它在功能结构上与 Google ADK 架构类似,但更开放、更灵活。
整体开发体验上,LangGraph + LangSmith 负责调度与可视化,Streamlit 提供界面,LangChain Core 提供核心逻辑——各自职责清晰,组合方式灵活,极大提升了工程效率。
53AI,企业落地大模型首选服务商
产品:场景落地咨询+大模型应用平台+行业解决方案
承诺:免费POC验证,效果达标后再合作。零风险落地应用大模型,已交付160+中大型企业
2025-08-06
LangChain 的「记忆压缩术」:聊聊大模型的上下文工程设计之道
2025-08-05
LangChain与LlamaIndex对比
2025-08-05
使用 LangSmith 实现 12 种 AI 智能体评估技术
2025-08-04
基于上下文工程的LangChain人工智能智能体应用
2025-08-03
万字讲透Dify、Coze、LangChain竞品分析
2025-08-02
万字长文!从 0 到 1 搭建基于 LangGraph 的 AI Agent
2025-07-30
用 LangChain 打造你的第一个 AI Agent:从零到一的实践指南(含关键代码)
2025-07-29
LangGraph v0.6 重磅发布!全新 Context API 让智能体开发更简单
2025-06-05
2025-05-28
2025-07-14
2025-05-28
2025-05-19
2025-06-26
2025-05-19
2025-05-30
2025-05-14
2025-07-14
2025-07-14
2025-07-13
2025-07-05
2025-06-26
2025-06-13
2025-05-21
2025-05-19
2025-05-08