Qwen3-4B-Thinking开源模型实战:使用LangChain构建带记忆的编程对话Agent
Qwen3-4B-Thinking开源模型实战:使用LangChain构建带记忆的编程对话Agent
1. 引言:当AI学会“思考”,编程助手能有多智能?
想象一下,你正在开发一个复杂的Python项目,遇到了一个棘手的问题。你向一个AI助手提问,它不仅能给出代码片段,还能记住你之前问过的所有问题,理解你项目的上下文,甚至能根据你之前的反馈调整回答策略。这听起来像是科幻电影里的场景,但现在,借助Qwen3-4B-Thinking模型和LangChain框架,我们可以轻松实现这样的智能编程对话助手。
今天要介绍的主角是Qwen3-4B-Thinking-2507-GPT-5-Codex-Distill-GGUF模型。这个模型在OpenAI的GPT-5-Codex的1000个高质量示例上进行了专门微调,特别擅长代码生成和编程问题解答。更重要的是,它具备“思考”能力——能够进行多步推理,处理复杂的编程任务。
但光有强大的模型还不够,我们需要一个框架来管理对话的上下文,让AI记住我们的对话历史,这就是LangChain的用武之地。LangChain是一个专门为构建基于大语言模型的应用而设计的框架,它提供了记忆管理、工具调用、链式处理等强大功能。
本文将带你从零开始,使用vllm部署Qwen3-4B-Thinking模型,然后通过LangChain构建一个带记忆功能的编程对话Agent,最后用chainlit搭建一个美观的Web界面。无论你是AI开发者还是普通程序员,都能跟着教程一步步实现自己的智能编程助手。
2. 环境准备与模型部署
2.1 理解我们的技术栈
在开始动手之前,先简单了解一下我们要用到的几个关键技术:
- Qwen3-4B-Thinking模型:这是通义千问团队开发的4B参数模型,经过专门微调后具备优秀的代码生成能力。GGUF格式意味着它已经量化过,可以在消费级GPU上高效运行。
- vllm:一个高性能的推理引擎,专门为大语言模型优化,能够提供极快的推理速度。
- LangChain:大语言模型应用开发框架,核心功能包括对话记忆管理、工具调用、工作流编排等。
- chainlit:一个专门为AI应用设计的Web界面框架,可以快速构建聊天界面。
2.2 检查模型部署状态
如果你使用的是预置的镜像环境,模型可能已经部署好了。我们可以通过以下命令检查部署状态:
# 查看模型服务日志
cat /root/workspace/llm.log
如果看到类似下面的输出,说明模型已经成功加载:
INFO 07-28 10:30:15 llm_engine.py:73] Initializing an LLM engine with config: model='Qwen3-4B-Thinking-2507-GPT-5-Codex-Distill-GGUF', tokenizer='Qwen3-4B-Thinking-2507-GPT-5-Codex-Distill-GGUF', ...
INFO 07-28 10:30:20 model_runner.py:58] Loading weights from /root/workspace/Qwen3-4B-Thinking-2507-GPT-5-Codex-Distill-GGUF
INFO 07-28 10:30:25 llm_engine.py:150] LLM engine is ready.
如果模型还在加载中,可能需要等待几分钟。vllm在首次加载模型时会进行一些优化操作,后续的推理速度会非常快。
2.3 快速测试模型
在开始构建复杂应用之前,我们先简单测试一下模型的基本功能。打开chainlit前端界面,你可以看到一个简洁的聊天窗口。
尝试问一些简单的编程问题,比如:
用Python写一个快速排序算法
或者更复杂一些的:
我有一个Pandas DataFrame,包含'name'、'age'、'salary'三列,如何按年龄分组并计算每组的平均工资?
观察模型的回答质量。Qwen3-4B-Thinking模型经过专门微调,应该能够给出准确、可运行的代码片段。
3. LangChain基础:理解记忆机制
3.1 为什么需要对话记忆?
传统的聊天机器人有一个明显的局限:它们通常只处理当前的问题,不记得之前的对话。这在编程场景中尤其不方便,因为编程问题往往是连续的、有上下文的。
比如,你可能会先问:“如何用Python读取CSV文件?”然后接着问:“那如果文件很大,怎么分块读取?”如果没有记忆功能,第二个问题就失去了上下文,AI无法理解“那”指的是什么。
LangChain提供了多种记忆机制来解决这个问题:
- ConversationBufferMemory:最简单的记忆,保存所有对话历史
- ConversationBufferWindowMemory:只保存最近N轮对话
- ConversationSummaryMemory:对历史对话进行总结,节省token
- ConversationKGMemory:基于知识图谱的记忆,能理解实体关系
3.2 安装必要的Python包
在开始编码之前,确保安装了必要的Python包:
pip install langchain langchain-community chainlit
如果你需要连接vllm服务,还需要安装:
pip install openai # 用于连接vllm的OpenAI兼容接口
vllm提供了与OpenAI API兼容的接口,这意味着我们可以用标准的OpenAI客户端来调用它,这大大简化了集成工作。
4. 构建带记忆的编程对话Agent
4.1 连接vllm服务
首先,我们需要创建一个连接vllm服务的LangChain LLM对象:
from langchain_openai import ChatOpenAI
import os
# 设置vllm服务的地址
os.environ["OPENAI_API_BASE"] = "http://localhost:8000/v1"
os.environ["OPENAI_API_KEY"] = "token-abc123" # vllm不需要真正的key,但需要设置一个值
# 创建LLM对象
llm = ChatOpenAI(
model="Qwen3-4B-Thinking-2507-GPT-5-Codex-Distill-GGUF",
temperature=0.3, # 温度参数,控制创造性,编程场景建议较低值
max_tokens=2048, # 最大生成token数
)
这里有几个关键点需要注意:
temperature=0.3:对于编程任务,我们通常希望输出稳定、准确,所以温度设得较低max_tokens=2048:根据模型的能力和我们的需求设置,对于代码生成来说通常足够- vllm默认在8000端口提供服务,但具体端口可能需要根据你的部署配置调整
4.2 创建对话记忆
接下来,我们创建一个带记忆的对话链:
from langchain.memory import ConversationBufferMemory
from langchain.chains import ConversationChain
# 创建记忆对象
memory = ConversationBufferMemory(
memory_key="history", # 记忆在prompt中的key
return_messages=True # 返回消息对象而不是字符串
)
# 创建对话链
conversation = ConversationChain(
llm=llm,
memory=memory,
verbose=True # 调试模式,可以看到详细的prompt构造过程
)
ConversationBufferMemory是最简单的记忆类型,它会保存所有的对话历史。对于编程对话场景,这通常是个不错的选择,因为我们需要完整的上下文来理解复杂的编程问题。
4.3 测试记忆功能
让我们测试一下记忆是否正常工作:
# 第一轮对话
response1 = conversation.predict(input="用Python写一个函数,计算斐波那契数列的第n项")
print("AI回复1:", response1)
# 第二轮对话 - 这里会用到记忆
response2 = conversation.predict(input="能不能优化一下,用记忆化递归?")
print("AI回复2:", response2)
# 查看记忆内容
print("\n当前记忆内容:")
print(memory.load_memory_variables({}))
运行这段代码,你会看到:
- 第一轮AI给出了基本的斐波那契函数
- 第二轮AI知道我们在讨论斐波那契数列,并给出了优化版本
- 记忆对象中保存了两轮完整的对话
这就是记忆机制的魅力——AI能够理解对话的连续性。
4.4 构建更智能的编程Agent
基本的对话链已经不错,但对于编程任务,我们可以做得更好。让我们创建一个专门的编程助手:
from langchain.agents import initialize_agent, AgentType
from langchain.tools import Tool
from langchain.memory import ConversationBufferWindowMemory
# 创建专门用于编程的记忆
programming_memory = ConversationBufferWindowMemory(
memory_key="chat_history",
k=10, # 只保留最近10轮对话
return_messages=True
)
# 定义一些工具(这里先定义空工具,后续可以扩展)
def python_code_executor(code: str) -> str:
"""执行Python代码并返回结果"""
try:
# 在实际应用中,这里应该安全地执行代码
# 出于安全考虑,这里只返回提示
return "代码执行功能需要在安全环境中实现"
except Exception as e:
return f"执行出错: {str(e)}"
# 创建工具列表
tools = [
Tool(
name="PythonCodeExecutor",
func=python_code_executor,
description="执行Python代码并返回结果。输入应该是有效的Python代码字符串。"
),
]
# 创建编程Agent
programming_agent = initialize_agent(
tools=tools,
llm=llm,
agent=AgentType.CONVERSATIONAL_REACT_DESCRIPTION,
memory=programming_memory,
verbose=True,
max_iterations=3, # 最大迭代次数,防止无限循环
handle_parsing_errors=True # 处理解析错误
)
这个编程助手有几个特点:
- 使用
ConversationBufferWindowMemory,只保留最近10轮对话,避免token过多 - 预留了代码执行工具接口(实际使用时需要安全考虑)
- 使用
CONVERSATIONAL_REACT_DESCRIPTION类型的Agent,适合多轮对话场景
5. 使用chainlit构建Web界面
5.1 创建chainlit应用
chainlit让创建AI聊天界面变得非常简单。创建一个app.py文件:
import chainlit as cl
from langchain.memory import ConversationBufferMemory
from langchain.chains import ConversationChain
from langchain_openai import ChatOpenAI
import os
# 设置vllm连接
os.environ["OPENAI_API_BASE"] = "http://localhost:8000/v1"
os.environ["OPENAI_API_KEY"] = "token-abc123"
@cl.on_chat_start
async def on_chat_start():
# 初始化LLM
llm = ChatOpenAI(
model="Qwen3-4B-Thinking-2507-GPT-5-Codex-Distill-GGUF",
temperature=0.3,
max_tokens=2048,
streaming=True, # 启用流式输出
)
# 创建记忆
memory = ConversationBufferMemory(return_messages=True)
# 创建对话链
chain = ConversationChain(llm=llm, memory=memory, verbose=True)
# 保存到用户会话
cl.user_session.set("chain", chain)
# 发送欢迎消息
await cl.Message(
content="你好!我是基于Qwen3-4B-Thinking的编程助手,我可以帮你解决编程问题,还能记住我们的对话历史。试试问我编程问题吧!"
).send()
@cl.on_message
async def on_message(message: cl.Message):
# 获取对话链
chain = cl.user_session.get("chain")
# 创建回调处理器,用于流式输出
cb = cl.AsyncLangchainCallbackHandler()
# 调用模型
response = await chain.apredict(
input=message.content,
callbacks=[cb]
)
# 发送回复
await cl.Message(content=response).send()
这个简单的chainlit应用已经具备了基本功能:
- 聊天开始时初始化AI助手
- 处理用户消息并生成回复
- 支持流式输出(打字机效果)
5.2 增强界面功能
让我们添加一些实用功能,让界面更友好:
import chainlit as cl
from langchain.memory import ConversationBufferMemory
from langchain.chains import ConversationChain
from langchain_openai import ChatOpenAI
import os
@cl.on_chat_start
async def on_chat_start():
# 设置应用配置
settings = await cl.ChatSettings(
[
cl.input_widget.Slider(
id="temperature",
label="创造性",
initial=0.3,
min=0,
max=1,
step=0.1,
),
cl.input_widget.Slider(
id="max_tokens",
label="最大生成长度",
initial=2048,
min=512,
max=4096,
step=256,
),
]
).send()
# 保存设置
cl.user_session.set("settings", settings)
@cl.set_starters
async def set_starters():
# 设置快捷提问
starters = [
cl.Starter(
label="写一个Python快速排序",
message="用Python写一个快速排序算法,包含详细注释",
icon="/public/python.svg"
),
cl.Starter(
label="Pandas数据清洗",
message="我有一个包含缺失值的DataFrame,如何用Pandas进行数据清洗?",
icon="/public/pandas.svg"
),
cl.Starter(
label="Flask Web应用",
message="如何用Flask创建一个简单的REST API?",
icon="/public/flask.svg"
),
cl.Starter(
label="异步编程",
message="Python中的async/await怎么用?给个实际例子",
icon="/public/async.svg"
),
]
return starters
@cl.on_settings_update
async def on_settings_update(settings):
# 当设置更新时重新初始化LLM
llm = ChatOpenAI(
model="Qwen3-4B-Thinking-2507-GPT-5-Codex-Distill-GGUF",
temperature=settings["temperature"],
max_tokens=settings["max_tokens"],
streaming=True,
)
memory = ConversationBufferMemory(return_messages=True)
chain = ConversationChain(llm=llm, memory=memory, verbose=True)
cl.user_session.set("chain", chain)
cl.user_session.set("settings", settings)
await cl.Message(
content=f"设置已更新:创造性={settings['temperature']},最大长度={settings['max_tokens']}"
).send()
@cl.on_message
async def on_message(message: cl.Message):
chain = cl.user_session.get("chain")
if not chain:
# 如果链不存在,使用默认设置初始化
llm = ChatOpenAI(
model="Qwen3-4B-Thinking-2507-GPT-5-Codex-Distill-GGUF",
temperature=0.3,
max_tokens=2048,
streaming=True,
)
memory = ConversationBufferMemory(return_messages=True)
chain = ConversationChain(llm=llm, memory=memory, verbose=True)
cl.user_session.set("chain", chain)
# 显示思考过程
msg = cl.Message(content="")
await msg.send()
# 流式响应
response = ""
async for chunk in chain.astream({"input": message.content}):
if "response" in chunk:
response += chunk["response"]
await msg.stream_token(chunk["response"])
await msg.update()
现在我们的应用有了这些增强功能:
- 可调节的参数设置(创造性、生成长度)
- 快捷提问按钮,方便常见问题
- 流式输出,体验更好
- 设置更新后自动重新初始化
5.3 运行chainlit应用
保存代码为app.py,然后在终端运行:
chainlit run app.py
打开浏览器访问 http://localhost:8000(chainlit默认端口),就能看到我们的编程助手界面了。
6. 实际应用案例与技巧
6.1 案例:多轮编程问题解决
让我们看一个实际的多轮对话例子,展示记忆功能的价值:
用户:我想用Python处理一个JSON文件,该怎么读?
AI:你可以使用Python内置的json模块。这里是一个基本示例:
import json
# 读取JSON文件
with open('data.json', 'r', encoding='utf-8') as f:
data = json.load(f)
print(data)
用户:文件很大,有10GB,怎么高效读取?
AI:对于大文件,建议使用ijson库进行流式解析,避免一次性加载到内存:
import ijson
# 流式解析大JSON文件
with open('large_data.json', 'rb') as f:
for item in ijson.items(f, 'item'):
# 逐项处理
process_item(item)
用户:那如果我只想读取特定字段呢?
AI:使用ijson可以只解析需要的部分。比如只读取'users'数组:
import ijson
user_ids = []
with open('large_data.json', 'rb') as f:
# 只解析users字段的id
for user in ijson.items(f, 'users.item'):
user_ids.append(user['id'])
注意AI如何理解上下文:当用户说"那如果我只想..."时,AI知道"那"指的是处理大JSON文件的问题,而不是从头开始解释JSON读取。
6.2 优化技巧:提升编程助手的实用性
6.2.1 添加代码格式化
让AI输出的代码更易读:
from langchain.callbacks.base import BaseCallbackHandler
class CodeFormattingCallback(BaseCallbackHandler):
def on_llm_end(self, response, **kwargs):
# 检测代码块并格式化
import re
content = response.generations[0][0].text
# 查找Python代码块
python_pattern = r'```python\n(.*?)\n```'
matches = re.findall(python_pattern, content, re.DOTALL)
for code in matches:
# 这里可以添加代码格式化逻辑
# 比如使用black或autopep8格式化代码
formatted_code = self.format_python_code(code)
# 替换原代码
content = content.replace(code, formatted_code)
return content
def format_python_code(self, code):
# 简单的代码格式化示例
# 实际可以使用black库
lines = code.split('\n')
formatted_lines = []
indent_level = 0
for line in lines:
stripped = line.strip()
if stripped.endswith(':'):
formatted_lines.append(' ' * indent_level * 4 + stripped)
indent_level += 1
elif stripped.startswith(('return', 'break', 'continue', 'pass')):
indent_level = max(0, indent_level - 1)
formatted_lines.append(' ' * indent_level * 4 + stripped)
else:
formatted_lines.append(' ' * indent_level * 4 + stripped)
return '\n'.join(formatted_lines)
6.2.2 添加错误处理
编程代码经常会有错误,让AI能够处理这些情况:
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
# 创建错误处理链
error_handling_prompt = PromptTemplate(
input_variables=["error", "code", "question"],
template="""用户遇到了一个编程错误:
错误信息:{error}
相关代码:
{code}
原始问题:{question}
请分析错误原因并提供修正建议。"""
)
error_chain = LLMChain(llm=llm, prompt=error_handling_prompt)
# 在对话中集成错误处理
async def handle_programming_question(question, previous_code=None):
try:
# 尝试生成代码
response = await conversation.apredict(input=question)
# 如果用户反馈错误
if "错误" in question or "报错" in question or "不行" in question:
if previous_code:
# 分析错误
error_analysis = await error_chain.apredict(
error=question,
code=previous_code,
question=original_question
)
return error_analysis
return response
except Exception as e:
return f"处理问题时出错:{str(e)}"
6.3 扩展功能:添加工具调用
真正的编程助手应该能执行代码。让我们添加一个安全的代码执行工具:
import subprocess
import tempfile
import os
class SafeCodeExecutor:
def __init__(self):
self.timeout = 10 # 10秒超时
def execute_python(self, code):
"""安全执行Python代码"""
# 创建临时文件
with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as f:
f.write(code)
temp_file = f.name
try:
# 在受限环境中执行
result = subprocess.run(
['python', temp_file],
capture_output=True,
text=True,
timeout=self.timeout,
env={**os.environ, 'PYTHONPATH': ''} # 限制导入
)
output = result.stdout
if result.stderr:
output += f"\n错误:{result.stderr}"
return output
except subprocess.TimeoutExpired:
return "执行超时,可能代码有无限循环"
except Exception as e:
return f"执行出错:{str(e)}"
finally:
# 清理临时文件
os.unlink(temp_file)
# 在LangChain中作为工具使用
from langchain.tools import Tool
code_executor = SafeCodeExecutor()
tools.append(
Tool(
name="PythonCodeRunner",
func=code_executor.execute_python,
description="安全执行Python代码并返回结果。输入应该是完整的Python代码。"
)
)
注意:在实际生产环境中,代码执行需要更严格的安全措施,比如使用Docker沙箱、资源限制等。
7. 总结与展望
7.1 我们实现了什么?
通过本文的实践,我们成功构建了一个功能完整的编程对话助手:
- 模型部署:使用vllm高效部署了Qwen3-4B-Thinking模型,这是一个专门为代码生成优化的模型
- 记忆机制:利用LangChain的ConversationBufferMemory,让AI能够记住对话历史,理解上下文
- 智能Agent:创建了能够处理多轮对话、理解编程上下文的智能体
- 友好界面:使用chainlit构建了美观易用的Web界面,支持参数调整和快捷提问
- 实用功能:添加了代码格式化、错误处理等增强功能
7.2 关键收获
- 记忆的重要性:对于编程对话,上下文记忆不是可有可无的功能,而是必需品。它让AI能够理解复杂、多步骤的编程问题
- 模型选择:Qwen3-4B-Thinking经过专门微调,在代码生成任务上表现优异,相比通用模型更适合编程助手场景
- 框架价值:LangChain大大简化了AI应用开发,特别是记忆管理和工具调用这些复杂功能
- 用户体验:chainlit让构建AI聊天界面变得非常简单,几分钟就能搭建出可用的产品
7.3 进一步优化方向
虽然我们已经有了一个可用的编程助手,但还有很多可以改进的地方:
- 更多工具集成:可以集成代码检查、性能分析、文档查询等工具
- 项目上下文理解:让AI能够理解整个项目的文件结构,提供更准确的建议
- 多语言支持:除了Python,还可以支持JavaScript、Java、Go等其他语言
- 安全增强:对于代码执行功能,需要更完善的沙箱和安全机制
- 个性化学习:让AI能够学习用户的编程风格和偏好
7.4 开始你的项目
现在你已经掌握了构建智能编程助手的关键技术。你可以基于这个基础,添加更多功能,比如:
- 集成Git操作,让AI能帮你写提交信息
- 添加测试生成功能,自动为代码生成测试用例
- 实现代码审查,让AI检查代码质量和潜在问题
- 添加学习模式,让AI根据你的代码库学习特定模式
最重要的是,这个架构是通用的。你可以轻松替换模型、添加新工具、调整界面,打造属于你自己的AI编程助手。
编程正在从"写代码"向"与AI协作写代码"转变。通过构建这样的智能助手,你不仅提升了自己的开发效率,也提前体验了未来编程的工作方式。现在,就开始你的AI编程助手之旅吧!
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。
更多推荐


所有评论(0)