Day12:从零开始写一个Agent项目(4)(完结)
开头
嘿,这里是惬鹤频道!
经过这几天的学习,我终于完成了Agent项目的全部内容。
芜湖!
芜湖!
芜湖芜湖!!
好了不闹了,这次要带来的是剩余的两个代码文件。这两个文件不是很重要,但是依旧需要对前面相关知识的整合,
还需要见见老朋友streamlit。
顺带说一下,这次的项目编写完毕后,在想出下一个项目要做什么之前,可能不会有太多新博客发布,感谢大家的支持!
我也许很快就会回归,希望后面还能和大家见面。请多多支持!
那让我们开始吧!
前端代码:app.py
"""
文件名:app.py
描述:利用streamlit进行网页端的编写
"""
# 依赖导入
import time
import streamlit as st
# 依赖文件导入
from agent.react_agent import ReactAgent
# 设置页面标题
st.title("智扫通机器人智能客服")
# 添加一条水平分割线,美化页面
st.divider()
# 初始化会话状态中的 Agent 实例(只会执行一次)
if "agent" not in st.session_state:
st.session_state["agent"] = ReactAgent()
# 初始化会话状态中的消息历史列表(只会执行一次)
if "message" not in st.session_state:
st.session_state["message"] = []
# 遍历历史消息,在页面上显示出来
for message in st.session_state["message"]:
st.chat_message(message["role"]).write(message["content"])
# 页面底部显示聊天输入框,用户输入后按回车触发
prompt = st.chat_input()
# 如果用户输入了内容
if prompt:
# 立即在界面上显示用户发送的消息
st.chat_message("user").write(prompt)
# 将用户消息追加到历史记录中
st.session_state["message"].append({"role": "user", "content": prompt})
# 用于收集 AI 响应的所有片段(完整内容)
response_messages = []
# 显示“思考中”的加载动画
with st.spinner("智能客服思考中。。。"):
# 调用 Agent 的流式执行方法,返回一个生成器
res_stream = st.session_state["agent"].execute_stream(prompt)
# 定义生成器函数,用于同时完成两件事:
# - 将每个响应块缓存到 cache_list 中
# - 将每个字符逐个 yield 出去,实现打字机效果
def capture(generator, cache_list):
for chunk in generator: # 每次得到一个响应块(可能是一个字符串)
cache_list.append(chunk) # 缓存完整块
for char in chunk: # 遍历块中的每个字符
time.sleep(0.01) # 控制字符输出速度(模拟打字效果)
yield char # 逐个字符输出到页面
# 在助手消息容器中实时流式输出 AI 的回答(逐字显示)
st.chat_message("assistant").write_stream(capture(res_stream, response_messages))
# 将完整的 AI 回答追加到历史记录中
st.session_state["message"].append({"role": "assistant", "content": response_messages[-1]})
# 强制页面重新运行(刷新界面),以便显示最新的历史消息
st.rerun()
我们的老朋友:streamlit,用来生成前端页面。
一、初始化部分(页面标题、分割线)
st.title:设置页面标题
st.divider:添加水平分隔线
二、会话状态初始化
st.session_state[“agent”]:单例模式,确保整个会话只有一个 ReactAgent 实例。
st.session_state[“message”]:存储对话历史(每一条消息为 {“role”: “user”/“assistant”, “content”: “…”})。
三、显示历史消息
遍历 st.session_state[“message”],用 st.chat_message(role).write(content) 渲染每条消息。
四、用户输入处理
st.chat_input():底部输入框,用户输入后触发 if prompt 块。
4.1 显示用户消息并记录
立即在聊天区域显示用户消息。
将用户消息追加到 session_state[“message”]。
4.2 调用 Agent 流式生成
res_stream = st.session_state[“agent”].execute_stream(prompt):获得生成器(每块可能是一个字符串片段)。
定义 capture 生成器:
遍历 generator 的每个块(chunk)。
将完整块缓存到 cache_list(用于最终记录完整回答)。
再将块拆解为字符,每个字符 time.sleep(0.01) 模拟打字机效果,然后 yield char。
st.chat_message(“assistant”).write_stream(capture(…)):逐字符流式输出。
4.3 保存完整回答并刷新
将完整回答(response_messages[-1],注意这里只取了最后一个块,存在 bug)追加到历史记录。
st.rerun():强制页面重新运行,以更新消息列表(实际上 Streamlit 在回调结束后会自动 rerun,这里手动调用确保立即刷新)。
代码文件:react_agent.py
"""
文件名:react_agent.py
描述:
"""
# 导入依赖
from langchain.agents import create_agent
from model.factory import chat_model
from utils.prompt_loader import load_system_prompts
from agent.tools.agent_tools import (rag_summarize, get_weather, get_user_id, get_user_location,
get_current_month, fetch_external_data, fill_context_for_report)
from agent.tools.middleware import monitor_tool, log_before_model, report_prompt_switch
# 创建智能体
class ReactAgent:
def __init__(self):
self.agent = create_agent(
model = chat_model,
system_prompt=load_system_prompts(),
tools=[rag_summarize, get_weather, get_user_id, get_user_location,
get_current_month, fetch_external_data, fill_context_for_report],
middleware=[monitor_tool, log_before_model, report_prompt_switch],
)
# 接受问题,返回一个调用智能体的格式
def execute_stream(self, query: str):
input_dict = {
"messages":[
{"role": "user", "content": query}
]
}
# context就是提示词切换的标记
for chunk in self.agent.stream(input_dict, stream_mode = "values", context={"report": False}):
latest_message = chunk["messages"][-1]
if latest_message.content:
yield latest_message.content.strip() + "\n"
关于代码的解析如下:
1.依赖导入
从 langchain.agents 导入 create_agent(官方推荐的 Agent 创建方法)
从 model.factory 导入预配置的聊天模型 chat_model
从 utils.prompt_loader 导入 load_system_prompts(加载系统提示词)
从 agent.tools.agent_tools 导入 7 个自定义工具(RAG、天气、用户信息、外部数据等)
从 agent.tools.middleware 导入 3 个中间件(监控、日志、提示词切换)
2.ReactAgent 类
init 方法
调用 create_agent 创建一个 Agent 实例,并存储在 self.agent 中。
传入参数:
model:聊天模型
system_prompt:系统提示词(定义 Agent 角色、规则)
tools:工具列表
middleware:中间件列表
execute_stream 方法
输入:用户问题 query
构造输入字典:{“messages”: [{“role”: “user”, “content”: query}]}
调用 self.agent.stream,指定 stream_mode=“values”(返回完整状态),并传递 context={“report”: False}(控制提示词切换)
遍历流式输出,每次取出 chunk[“messages”][-1](最新消息)
如果消息内容非空,则 yield latest_message.content.strip() + “\n”(逐块产出内容,每块后加换行)
3.关键设计点
封装复杂性:将 create_agent 的配置细节隐藏在类内部,外部只需调用 execute_stream 即可获得流式回复。
流式输出:使用生成器 yield,适配 Streamlit 的 write_stream 方法,实现实时打字效果。
上下文传递:context={“report”: False} 用于控制 Agent 的行为(如是否切换到报告生成模式),中间件可以读取该上下文并修改提示词或执行逻辑。
4.与前端(app.py)的协作
app.py 中 st.session_state[“agent”] = ReactAgent() 创建 Agent 实例。
用户输入后,调用 res_stream = st.session_state[“agent”].execute_stream(prompt) 获得生成器。
使用 st.chat_message(“assistant”).write_stream(…) 实时显示流式输出。
四个配置文件
嗯,是不是忘记了什么?
对了,那四个配置文件我还没和大家分享,下面一起发出来。
配置文件:agent.yml
external_data_path: data/external/records.csv
配置文件:chroma.yml
collection_name: agent
persist_directory: chroma_db
k: 3
data_path: data
md5_hex_store: md5.txt
allow_knowledge_file_type: ["txt", "pdf"]
chunk_size: 200
chunk_overlap: 20
separators: ["\n\n", "\n", ".", "!", "?", "。", "!", "?", " ", ""]
配置文件:prompts.yml
main_prompt_path: prompts/main_prompt.txt
rag_summarize_prompt_path: prompts/rag_summarize.txt
report_prompt_path: prompts/report_prompt.txt
配置文件:rag.yml
chat_model_name: qwen3-max
embedding_model_name: text-embedding-v4
结尾
那么,到这里,所有的代码文件都完成了分享和解析,如果你对这个项目有兴趣,可以到如下GitHub网址进行下载:
https://github.com/QHzzy035/RoboCare-Agent
那么,下次再见了!
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)