欢迎光临
我们一直在努力

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: qwen3max
embedding_model_name: textembeddingv4

结尾

那么,到这里,所有的代码文件都完成了分享和解析,如果你对这个项目有兴趣,可以到如下GitHub网址进行下载:
https://github.com/QHzzy035/RoboCare-Agent
那么,下次再见了!

赞(0)
未经允许不得转载:171主机测评 » Day12:从零开始写一个Agent项目(4)(完结)
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址