Last chance! 50% off DataCamp Premium
Sale ends in
GLM-5-Turbo 专为智能体式工作流设计,模型能够协调多步执行。它针对理解复杂指令、可靠调用工具以及生成可被系统消费的结构化输出做了优化。
在本教程中,我们将构建一个实时浏览器智能体:主动浏览网页、提取相关信息,并为用户生成干净明了的摘要。完成后,您将获得一套可复用的架构,不仅适用于航班,还可扩展到任何真实世界的搜索与抽取任务。
您可以在下方视频中看到我们的 GLM-5 Turbo 项目演示:
GLM-5-Turbo 旨在支持真实场景下的智能体工作流。它提供多种思考模式,用户可根据任务在更快响应与更深层推理之间进行权衡。
对于交互式应用,它还支持流式输出,使响应能以逐 token 传输,带来更灵敏的用户体验。
其核心优势之一是函数调用,模型可以可靠地调用外部工具。模型还支持 JSON 等结构化输出,便于与下游系统和 UI 层集成。
对于较长的工作流,GLM-5-Turbo 提供上下文缓存,避免重复处理相同信息,从而在长时交互中保持性能。它也支持通过 MCP(Model Context Protocol)与外部系统集成,让模型连接更多工具与数据源。
GLM-5-Turbo 针对智能体环境做了深度优化,尤其适用于类似OpenClaw 的场景,任务涉及多步骤、多工具与长链路执行。
这种优化提升了以下关键能力:
在实践中,GLM-5-Turbo 既足够快以支持交互系统,又能在多步工具调用场景中保持可靠,正好契合构建浏览器智能体的需求。
在本教程中,我们将创建一个 Streamlit 应用,充当轻量级出行助手。用户输入诸如“查找下周五至周一从 SFO 到 JFK 的最便宜航班”之类的查询。系统将:
从高层来看,系统由四个协同工作的组件组成。
llm_client.py),用于向 Z.AI 或 OpenRouter 发送对话补全请求。browser_tools.py),提供搜索、打开页面与提取内容等操作。agent.py),将模型与工具连接为多步工作流。app.py),提供交互与可视化。完整源码:https://github.com/AashiDutt/GLM5_TURBO
在构建应用之前,请确保具备以下条件:
安装所需库:
pip install streamlit pandas python-dotenv requests playwright
playwright install chromium
app.py 使用了 Streamlit、pandas、dotenv 和自定义模块;而 browser_tools.py 使用 requests 与 Playwright。API 调用通过 llm_client.py 中的 requests 发起。
现在,运行以下命令创建一个存放 API 密钥的 .env 文件:
cp .env.example .env
选择您偏好的 API 端点并填入 API 密钥:
.env
PROVIDER=zai
# Direct Z.AI API
ZAI_API_KEY=Your Z.AI API key
ZAI_BASE_URL=https://api.z.ai/api/paas/v4/chat/completions
ZAI_MODEL=glm-5-turbo
# OpenRouter
OPENROUTER_API_KEY=Your OpenRouter API key
OPENROUTER_BASE_URL=https://openrouter.ai/api/v1/chat/completions
OPENROUTER_MODEL=z-ai/glm-5-turbo
# Browser
HEADLESS=true
MAX_STEPS=10
同样地,Playwright 浏览器在 browser_tools.py 中通过如下构造函数启动:
class BrowserTools:
def __init__(self, headless: bool = True) -> None:
self.playwright = sync_playwright().start()
self.browser = self.playwright.chromium.launch(headless=headless)
self.context = self.browser.new_context(viewport={"width": 1440, "height": 1100})
self.page = self.context.new_page()
该代码块利用 Playwright 初始化一个完全可控的浏览器环境,供智能体与网页交互。
它会启动 Playwright 引擎、打开一个 Chromium 浏览器、创建隔离会话,并在新页面中执行所有操作。
通过该设置,智能体可浏览真实网站、观察实时内容,并基于所见作出决策。
接下来,我们将设置支持直接使用 Z.AI 与 OpenRouter 的 LLMClient,二者共享同一接口。构造函数中会选择特定提供商的端点。由于价格相近,本演示您可任选其一。
关键设置如下:
class LLMClient:
def __init__(
self,
provider: str,
api_key: str,
model: str,
app_name: str = "GLM-5 Browser Agent Demo",
app_url: str = "http://localhost:8501",
) -> None:
self.provider = provider.lower().strip()
self.api_key = api_key
self.model = model
if self.provider == "zai":
self.url = os.getenv("ZAI_BASE_URL", "https://api.z.ai/api/paas/v4/chat/completions")
elif self.provider == "openrouter":
self.url = os.getenv("OPENROUTER_BASE_URL", "https://openrouter.ai/api/v1/chat/completions")
该初始化过程会选择合适的 API 端点(Z.AI 或 OpenRouter),同时保持接口一致。
它会保存 API 密钥、模型与端点 URL,使系统其余部分无需关心提供商差异即可调用模型。
随后,create_chat_completion() 会组装请求载荷,在需要时附带工具,并解析返回的工具调用。
payload: Dict[str, Any] = {
"model": self.model,
"messages": messages,
"temperature": temperature,
"max_tokens": max_tokens,
"stream": False,
}
if tools:
payload["tools"] = tools
payload["tool_choice"] = "auto"
上述载荷定义了发送给模型的请求,包括对话上下文与生成参数。通过附加 tools 并启用 tool_choice="auto",模型可自行决定何时调用外部函数。
在 agent.py 中,我们定义了模型可调用的工具模式。这些工具包括 search_web, open_url、open_search_result、extract_page、extract_flight_cards、click、type_text 与 wait_for_text。
以下是一个工具定义示例:
{
"type": "function",
"function": {
"name": "search_web",
"description": "Search the public web for flight result pages or travel aggregators.",
"parameters": {
"type": "object",
"properties": {
"query": {"type": "string"},
"max_results": {"type": "integer", "default": 8},
},
"required": ["query"],
},
},
}
该模式告知模型工具的作用、期望的输入与调用方式。通过如此定义工具,我们无需硬编码流程,模型可在需要时动态发起网页搜索。
在执行端,这些工具实现在 browser_tools.py 中。本演示采用 DuckDuckGo 的 HTML 端点以存储当前结果链接,便于后续导航。
def search_web(self, query: str, max_results: int = 8) -> Dict[str, Any]:
url = f"https://html.duckduckgo.com/html/?q={quote_plus(query)}"
headers = {"User-Agent": "Mozilla/5.0"}
resp = requests.get(url, headers=headers, timeout=30)
resp.raise_for_status()
html = resp.text
上述 search_web() 函数通过获取原始 HTML 并解析,实现轻量网页搜索,避免依赖付费搜索 API。它还会在内部存储结果,便于智能体在后续步骤中导航。
在明确了智能体如何与网页交互后,下一步是查看它如何通过网页搜索提取航班信息。
一旦模型找到有潜力的来源,就需要进行检查。我们可以使用(步骤 3 的)浏览器工具来提取原始页面快照。
我们将使用 open_url() 工具加载网页,并返回包含标题、URL 与可见文本的快照。
def open_url(self, url: str) -> Dict[str, Any]:
self.page.goto(url, wait_until="domcontentloaded", timeout=45000)
self.page.wait_for_timeout(1200)
return self._page_snapshot()
更广泛的提取可使用下方 extract_page() 工具,它会返回页面快照及当前页面收集到的一组链接。
def extract_page(self, max_chars: int = 7000, link_limit: int = 20) -> Dict[str, Any]:
snapshot = self._page_snapshot(max_chars=max_chars)
snapshot["links"] = self._collect_links(limit=link_limit)
self.current_links = snapshot["links"]
return snapshot
这很有用,因为模型不必总是盲点乱点。它可以先检查当前页面文本与可用链接,再决定下一步。
演示的关键部分是 extract_flight_cards() 函数,它基于可见页面文本并用正则表达式模式检测价格、时间、时长与经停。
核心逻辑如下:
money_pattern = re.compile(r"(?:\$|USD\s?)(\d{2,5})")
time_pattern = re.compile(r"\b\d{1,2}:\d{2}\s?(?:AM|PM|am|pm)?\b")
duration_pattern = re.compile(r"\b\d{1,2}\s?h\s?\d{0,2}\s?m?\b|\b\d{1,2}\s?hr\s?\d{0,2}\s?min?\b", re.I)
stop_pattern = re.compile(r"nonstop|1 stop|2 stops|3 stops", re.I)
这些正则模式相当于常见航班信号的“探测器”。money_pattern 捕获如 $299 或 USD 299 的价格格式,而 time_pattern 识别出发与到达时间。
类似地,duration_pattern 提取行程时长,stop_pattern 判断航班是否直飞或有中转。您也可以为任意用例构建相似模式。
定义这些信号后,函数会扫描小块文本并寻找有意义的组合。一旦匹配,就构建一个结构化的航班卡片:
card = {
"snippet": joined[:350],
"price_usd": int(price_match.group(1)),
"times": times[:2],
"duration": duration.group(0) if duration else None,
"stops": stops.group(0) if stops else None,
}
上述代码将原始文本转换为航班选项的结构化表示:抽取价格、将时间条目限制为最相关的两项,并妥善处理缺失字段。同时保留原始上下文,便于调试。
完成抽取后,智能体即可将非结构化页面内容转化为可用数据。接下来我们看看这些数据如何在智能体循环中流转,驱动多步决策。
实际的智能体行为封装在 BrowserAgent 类(位于 agent.py 内)中。该类将提示与工具模式发送给模型;判断模型是否要调用工具;执行该工具;将工具结果追加回对话;并持续迭代直至产生最终答案。
主循环如下:
for step in range(1, self.max_steps + 1):
assistant = self.llm.create_chat_completion(
messages=messages,
tools=TOOLS,
temperature=0.2,
max_tokens=2200,
enable_thinking=False,
)
if assistant.tool_calls:
...
result = self.tool_runner(tc.name, tc.arguments)
...
continue
final_content = assistant.content.strip()
if final_content:
yield {"type": "final", "step": step, "content": final_content}
return
该循环是演示的核心。
每一步,模型都会接收当前对话状态并决定下一步操作。若返回工具调用,则通过 tool_runner 执行,并将结果反馈到循环中进入下一轮。
该流程会持续,直到模型不再调用工具而是生成最终响应。
循环还会设置最大步数限制,防止无限执行,确保流程可控高效。
在 app.py 中,我们将模型请求的工具名称映射到实际的浏览器方法。该映射由 make_tool_runner() 函数实现:
def make_tool_runner(browser: BrowserTools):
def run(name: str, args: Dict[str, Any]):
if name == "search_web":
return browser.search_web(**args)
if name == "open_url":
return browser.open_url(**args)
if name == "open_search_result":
return browser.open_search_result(**args)
if name == "extract_page":
return browser.extract_page(**args)
if name == "extract_flight_cards":
return browser.extract_flight_cards(**args)
该函数充当模型与浏览器之间的分发器。模型仅返回工具名与参数,run() 会将其翻译为对 BrowserTools 实例的函数调用。
在智能体可以成功调用并执行工具后,下一步是构建把所有部分串联起来的用户界面。
Streamlit 界面将浏览器智能体包装为交互式应用。它收集用户的行程细节、启动智能体、流式展示中间步骤,并最终渲染摘要及包含最佳航班选项的结构化表格。完整 UI 流程实现在 app.py 中。
st.set_page_config(page_title="GLM-5 Browser Agent", page_icon="", layout="wide")
st.title("GLM-5-Turbo Real-Time Browser Agent")
st.subheader("Trip Request")
origin = st.text_input("Origin", value="SFO")
destination = st.text_input("Destination", value="JFK")
depart_date = st.date_input("Depart date")
return_date = st.date_input("Return date")
adults = st.number_input("Adults", min_value=1, max_value=9, value=1)
natural_request = st.text_area(
"Optional natural language request",
value="Find the cheapest reasonable flight options and summarize the best ones.",
height=120,
)
user_query = (
f"Find flight options from {origin} to {destination} for {adults} adult(s). "
f"Departure date: {depart_date}. Return date: {return_date}. "
f"User preference: {natural_request}"
)
run = st.button("Run browser agent", type="primary", use_container_width=True)
if run:
if not api_key:
st.error("Please enter an API key in the sidebar.")
st.stop()
llm = LLMClient(provider=provider, api_key=api_key, model=model)
event_box = st.container(border=True)
result_box = st.container(border=True)
all_events = []
with managed_browser(headless=headless) as browser:
agent = BrowserAgent(llm=llm, tool_runner=make_tool_runner(browser), max_steps=max_steps)
for event in agent.stream(user_query):
all_events.append(event)
with event_box:
if event["type"] == "status":
st.write(f"**Step {event['step']}** — {event['content']}")
elif event["type"] == "tool_call":
with st.expander(f"Step {event['step']} • Tool call: {event['tool']}", expanded=False):
st.json(event["arguments"])
elif event["type"] == "tool_result":
with st.expander(f"Step {event['step']} • Tool result: {event['tool']}", expanded=False):
st.json(event["result"])
elif event["type"] == "final":
st.success("Agent finished.")
final_event = next((e for e in reversed(all_events) if e["type"] == "final"), None)
final_text = final_event["content"] if final_event else ""
with result_box:
st.subheader("Final Answer")
parsed = parse_final_json(final_text)
if parsed:
st.markdown(render_final_markdown(parsed))
options_df = build_options_dataframe(parsed)
if not options_df.empty:
st.subheader("Top options table")
st.dataframe(
options_df,
use_container_width=True,
column_config={
"Link": st.column_config.LinkColumn("Link", display_text="Open source"),
},
)
else:
st.markdown(final_text)
下面说明 Streamlit 界面如何将各组件融合:
st.set_page_config() 与 st.title() 定义了应用整体布局与标题;st.text_input()、st.date_input()、st.number_input() 与 st.text_area() 等输入控件用于采集行程细节。Run browser agent 按钮,应用先检查 API 密钥是否可用,然后初始化 LLMClient。接着为中间日志与最终结果创建独立容器,并开启受管的 Playwright 浏览器会话。在该会话中,BrowserAgent 开始流式输出事件,UI 将逐条展示状态更新、工具调用与工具结果。render_final_markdown() 展示,而 build_options_dataframe() 则把结构化的航班选项转换为带可点击来源链接的表格。完成该步骤后,您可以将全部保存为 app.py 并通过以下命令启动应用:
streamlit run app.py

该项目之所以适合作为真实演示,是因为它使用了实际的浏览器搜索结果与工具调用。同时,它仍是原型:航班网站动态性强、页面噪声多,且部分平台使用反爬虫保护。
当前抽取逻辑有意保持启发式,从而使系统简单易懂。
更重要的是,这套方案并不局限于航班。稍作调整(主要是更新提示词与抽取逻辑),您即可复用同一架构来构建购物、研究、新闻聚合或任何需要浏览并汇总实时信息的智能体。
在本教程中,我们使用 GLM-5-Turbo、Playwright 与 Streamlit 构建了一个实时浏览器智能体。模型充当规划者,浏览器工具是执行层,而 UI 则将结构化结果转化为用户友好的呈现。
最重要的结论是,价值并非仅来自模型本身,而是来自将模型与受控工具层和智能体循环相结合。
此外,本项目还有多种可扩展方向:通过添加站点特定解析器改进抽取;扩展到酒店或行程;或融入用户偏好(如直飞或偏好航空公司)。这些都能帮助您将该演示升级为成熟的浏览器扩展。
不需要。本项目无需 GPU,因为模型通过 API(Z.AI 或 OpenRouter)访问。所有重计算都在提供商的基础设施上完成,您的本地机器仅负责 UI 与浏览器自动化。
API 更可靠且结构化,但受限于预定义数据,并且通常需要付费接入。相比之下,浏览器智能体可以:
不过,在生产系统中,通常采用混合方案(智能体 + API)是最佳选择。
许多旅游网站使用:
由于本演示依赖通用浏览器自动化与启发式抽取,因此可能无法在所有网站上稳定工作。
JSON 在内部用于调试、结构化处理、排序与筛选。然而终端用户更偏好可读性强的摘要,因此系统会将结构化输出转换为用户友好的呈现形式。
我是一名 Google Developers 机器学习(生成式 AI)领域的专家、Kaggle 三项专家,以及 Women Techmakers 大使,拥有 3 年以上的技术从业经验。2020 年我共同创办了一家健康科技初创公司,目前在佐治亚理工学院攻读计算机科学硕士,专攻机器学习。
Top DataCamp Courses
课程
课程
课程