新闻详情

新闻详情

首页 / 资讯中心 / 详情

OpenCLI二次开发实战:从网页抓取到无头浏览器,打造AI Agent工具链

发布时间:2026/9/30 9:43:40来源:尧图网络
OpenCLI二次开发实战:从网页抓取到无头浏览器,打造AI Agent工具链
1. OpenCLI 的真正价值在于它是可插拔的 Agent 运行时OpenCLI 这个名字最近在开发圈频率越来越高但我发现绝大多数人只是把它当成命令行里一个对话窗口在用——问几个问题、让它写两段代码就收工。这实际上是大材小用。OpenCLI 真正的价值不是内置聊天能力而是它预留的二次开发接口你可以注册自己的工具函数让 AI 在对话过程中自动调用这些函数从而连接任意网站、读取实时数据、提交表单、触发远程操作。这才是它作为 AI Agent 运行时和普通聊天壳子的本质区别。我在第一次深入跑通 OpenCLI 的二次开发流程时最大的感受是一句话它把让 AI 干活这件事从理想拉到了工程可落地的距离。本文不打算讲 API 文档里已有的东西而是把我从环境搭建、工具注册、网页抓取、无头浏览器接入到踩了一堆坑之后总结出的完整实战路径写出来。适合两类读者一是想让 AI 能主动访问外部网站做信息收集的开发者二是想把现有命令行工具改造成 AI 可调用能力的团队开发者。1.1 原生工具覆盖不了任意网站这个目标先澄清一个边界问题OpenCLI 开箱即用的时候通常只带少量基础工具比如执行 Shell 命令、读写本地文件、调用少数内置 API。这些能力解决通用问题没问题但一旦遇到帮我抓取这个网址的内容并总结去那个系统里帮我查一下订单状态把这份数据提交到这个表单这类需求原生工具要么做不到要么只能做一半。原因不是 OpenCLI 不努力而是它的设计哲学就是核心运行时 外部工具扩展。开发者拿到的是一个带函数调用function calling能力的框架而不是一个什么都内置的巨型工具。你要连接目标网站 A就得为 A 写一个抓取解析器要连接系统 B就得为 B 写一个接口封装。这种需要什么扩展什么的模式恰恰是二次开发的核心工作方式也是它能保持轻量和安全的前提。1.2 二次开发的最小模型工具注册-模型调用-结果回传理解 OpenCLI 二次开发只需要抓住一个回路用户提出目标 → LLM 选择工具 → 运行时执行工具函数 → 返回值注入上下文 → LLM 继续推理。这个回路每转一圈AI 就完成一步具体的动手操作。工具函数本身没有任何魔法它就是一段普通代码接收 JSON 参数、返回 JSON 结果。LLM 能决定调哪个工具、传什么参数全靠你在注册阶段提交的工具描述——描述写得好不好直接决定模型能不能正确选择工具。这比许多人想象的要朴素但也正因为朴素工程上才稳定可控。后面我会用完整代码演示这套回路怎么建立起来。2. 开发环境准备从安装到第一个自定义工具跑通二次开发的第一步不是写业务逻辑而是把最小闭环跑通。这一步的杂音最多很多人一上来就掉进依赖冲突和配置路径的坑里下面给一套我实测可复现的路径。2.1 环境依赖Python 版本与核心库OpenCLI 的二次开发接口在 Python 生态里最成熟建议直接用 Python 3.10 以上版本。我本地用的是 Python 3.11跑下来没有任何兼容问题。核心依赖就两个opencli 主程序负责 CLI 交互、LLM 调用和工具注册。针对网页抓取的辅助库后续用到httpx和trafilatura。# 创建虚拟环境避免污染系统 Python python -m venv .venv source .venv/bin/activate # 安装 OpenCLI 与开发用依赖 pip install opencli httpx trafilatura一个小提醒务必用虚拟环境。OpenCLI 依赖的pydantic和typer版本比较敏感直接装进系统环境很容易跟其他项目打架。我带着教训说这句第一次图省事跳过虚拟环境结果光解决依赖冲突就花了一晚上。2.2 工程目录结构与配置文件OpenCLI 的二次开发遵循一个约定你写工具函数然后在配置里注册它。我习惯的目录结构是这样my_opencli_project/ ├── tools/ │ ├── __init__.py │ ├── web_reader.py │ └── web_operator.py ├── config.yaml └── main.pyconfig.yaml是 OpenCLI 读取的注册入口告诉运行时哪些 Python 函数可以作为工具暴露给 AI。实际内容大致长这样model: provider: openai-compatible base_url: http://your-api-endpoint api_key: sk-xxx model_name: qwen-max tools: - module: tools.web_reader function: read_web_page description: 抓取指定URL网页内容并提取正文返回纯文本。配置文件虽然简单但决定了模型地址、工具来源和描述信息。如果你的 LLM 走的是兼容 OpenAI 接口的网关现在大部分国内模型服务都提供兼容端点这个配置可以直接改base_url和api_key接上。建议先用本地或私有网关调通链路再切回线上模型能省很多排查时间。2.3 第一个工具函数 hello_web完整代码与注册流程下面这个函数是最小的可运行案例作用是抓取一个网页并返回可读文本# tools/web_reader.py import httpx from trafilatura import extract async def read_web_page(url: str, max_chars: int 4000) - dict: 抓取网页正文并截断到指定长度。 async with httpx.AsyncClient(timeout15, follow_redirectsTrue) as client: response await client.get( url, headers{User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36}, ) response.raise_for_status() content response.text text extract(content) or 无法从该页面提取正文。 return {content: text[:max_chars], status_code: response.status_code}注意几个要点函数签名里的url和max_chars是给 LLM 看的参数参数命名要语义化且给出默认值模型才不容易传错。返回值必须是可 JSON 序列化的字典不能返回自定义对象。max_chars的默认值配合截断逻辑非常重要这是防止 token 爆炸的第一道防线。注册完成后在 OpenCLI 里输入打开 https://example.com 并总结内容模型会自己决定调用read_web_page然后把返回的正文摘要给你。2.4 验证工具是否被正确加载配置写完之后最容易踩的坑是工具没加载成功但是 CLI 不报错。我建议先跑一行命令确认opencli tools list如果能看到你注册的函数名说明注册链路是通的。看不到就回去检查两样函数是否在tools/web_reader.py里定义以及module路径是否写错。这个检查要养成肌肉记忆后面每加一个工具都先opencli tools list再测试能省下大量调试时间。3. 连接网站的第一层能力让 AI 读得懂任意网页把连接网站细化一下第一层需求永远是读取。AI 要能理解页面内容前提是你能把页面里的有效信息提取出来、洗干净、再喂给模型。这一节是整个二次开发链路的地基地基不牢后面全是幻觉。3.1 抓取层requests 还是 httpx抓网页第一步是发 HTTP 请求。requests大家都熟但我在 OpenCLI 工具函数里默认用httpx原因只有一个异步支持。Agent 在运行时可能会并发调用多个工具一步步用同步requests两个抓取任务就得排队体验差一大截。httpx的 API 和requests几乎一样学习成本基本为零。另一个细节是follow_redirectsTrue。很多网站做了跳转http → https、www → 非 www、短链到长链不启用这个参数你抓到的常常是 301 空壳页面。用上之后response.url才是最终地址也方便后续做 URL 规范化。3.2 解析层从正则到可读性提取拿到 HTML 之后最直观的方案是正则匹配正文但这是个典型的看着简单、实际巨坑的方案。页面结构千变万化脚本样式一大坨正则只会把 AI 的上下文撑爆。我在这个环节用的是trafilatura库它是专门做文章正文提取的底层算法把标题、段落、表格按可读性打分筛出来效果比我手写过的好两个数量级。from trafilatura import extract # 传入 HTML 字符串得到干净的正文文本 text extract(html_content, include_commentsFalse, include_tablesTrue)关于参数配置我实测建议开启表格提取。技术文档和对比页面里表格承载的信息密度远高于正文段落留给 AI 更重要。3.3 工具契约输入输出参数的设计准则工具函数的输入输出设计直接决定模型调用准确性。我总结了四个准则都是从踩坑里倒推出来的参数必须有清晰的 description。函数签名里url: str对模型没有任何帮助你要加上待抓取的完整 URL包含协议头例如 https://example.com模型才敢放心传值。输出结构要扁平。不要嵌套太深模型要访问result.data.list[0].title和访问result[body]的成本天差地别。不要默认模型懂你的业务术语。工具名能动词开头就别用名词fetch_stock_price永远比stock_data好用。失败时返回错误结构而不是抛异常。工具函数抛异常OpenCLI 的运行时可能只看得到一句空的错误信息但如果你把错误原因放进返回值的error字段模型能看到并自动调整策略。后面专门讲这个坑。3.4 实测让 AI 总结一篇技术博客我用一个真实例子来收束这一层让 AI 总结一篇英文技术博客。注册好read_web_page之后在 OpenCLI 里输入 打开 https://www.gradio.app/guides/quickstart 这篇文章用中文总结核心内容实际执行结果是模型先调read_web_page拿到正文后用中文输出结构化总结。整个过程十几秒抓取耗时主要是网络延迟文本提取基本毫秒级。效果稳定之后我马上做了一件事把工具函数返回的正文保留前 4000 字符并按图表形式输出让模型可以基于同一份read_web_page扩展出翻译写摘要提取关键词等一系列动作。一次抓取反复消费这是把工具函数做成通用基础能力的标准姿势。4. 连接网站的第二层能力让 AI 操作任意网页只读网页解决信息获取但很多场景需要 AI 真正动手——登录后查询、填表单、点按钮、翻页。这就是第二层能力操作网页。4.1 为什么只读不够还得会写举个最常见的例子查个人订单信息。订单数据不在静态 HTML 里而是藏在登录后的用户中心。你要先输入账号密码、再点查询按钮最后等数据渲染完成。这套动作HTTP 请求仿不来因为涉及登录态和 JavaScript 渲染。解决办法是引入无头浏览器Headless Browser让 AI 拥有一个隐身浏览器来模拟真人操作。4.2 Playwright 无头浏览器接入最小可用代码我用的是 PlaywrightPython 接口稳定且异步支持好。先装依赖pip install playwright playwright install chromium然后注册一个查询工具完整流程包括打开页面、填写表单、点击按钮、等待结果# tools/web_operator.py from playwright.async_api import async_playwright async def query_order_by_phone(phone: str, start_date: str) - dict: 模拟用户操作进入订单查询页按手机号查询指定日期之后的订单。 参数: phone: 11位手机号 start_date: 日期格式 YYYY-MM-DD查询此日期及之后的订单 async with async_playwright() as p: browser await p.chromium.launch(headlessTrue) page await browser.new_page() try: await page.goto(https://some-site.com/orders, timeout30000) await page.fill(input[namephone], phone) await page.fill(input[namestart], start_date) await page.click(button[typesubmit]) await page.wait_for_selector(table.result, timeout15000) rows await page.query_selector_all(table.result tr) items [] for row in rows[:20]: cells await row.query_selector_all(td) texts [await cell.inner_text() for cell in cells] if texts: items.append(texts) return {orders: items} finally: await browser.close()这段代码里值得抠细节的有三处wait_for_selector等待的是结果出现而不是固定 sleep这比死等几秒靠谱得多browser.close()放在finally里防止异常时浏览器进程泄漏headlessTrue表示不弹出可见窗口服务器部署时必须有这个参数。如果你调试时想看到每一步操作把headless暂时改成False打开一个可见的浏览器窗口能直观看到 AI 的操作过程。4.3 把操作序列封装成高内聚的 Agent 工具记住一个原则工具越内聚模型越好用。不要注册一堆填用户名点登录点查询的原子操作让模型自己去编排这会让对话轮次暴涨且错误率飙升。反过来把完整动作链封装成一个大工具输入只有几个必要参数模型只需做一次调用。比如上面这个按手机号查询订单就是标准内聚工具。你甚至可以更进一步把查询订单和下载导出 Excel拆成两个工具让模型自由组合。这里的取舍标准我一直沿用一句话用户目标是一次调用完成的任务就封装成一个工具需要用户决策的分支才暴露成多个工具。工具粒度太细Agent 容易在中间步骤迷路粒度太粗又失去灵活性这个度要靠实际业务场景反复试。5. 连接网站的第三层能力直连 JSON API 构建稳定数据通道网页操作是人肉模拟的兜底方案但稳定性不如直连 API。很多网站背后其实藏着结构化的 JSON 接口如果能发现并利用它们AI 拿到的数据质量会高一大截。这一层适合对可靠性要求高的数据采集场景。5.1 接口发现从页面请求里找到真正的数据源打开浏览器开发者工具切到 Network 面板勾选 Fetch/XHR 过滤然后正常操作页面。你会看到许多 AJAX 请求绝大多数是 JSON 格式。找到那个返回完整业务数据的地址它就是你要对接的 API。以商品搜索为例页面可能长这样但数据全来自GET https://api.example-shop.com/v1/search?q手机page1拿到接口后先验证三个问题返回结构是否稳定、是否需要登录鉴权、是否有分页和限流。验证通过的话这个接口比解析 HTML 稳定十倍因为前端页面一改版你就得改解析规则而 API 通常受版本保护。5.2 鉴权与 Cookie 联动直连 API 最麻烦的是登录态。实践中常用两种处理方式如果接口只认 Cookie先从 Playwright 里面完成登录用context.cookies()拿到 cookie 字符串传给httpx.AsyncClient(cookiescookie_dict)。这样登录用浏览器搞定、数据用 API 直连兼顾稳定性和通过率。如果接口有独立 token把获取 token 封装成单独的函数在工具函数里先取 token 再请求。注意缓存 token别每次调用都重新签。建议把 Cookie 或 token 通过环境变量或者密钥管理服务注入不要写死在代码里。写死一时爽换密钥火葬场。5.3 限流、重试与数据增量更新API 直连的坑主要不是拿不到数据而是拿太猛被限制。我有一套通用策略限流工具函数内部维护一个简单的令牌桶比如每秒钟最多 5 个请求。超过就等一下再发。这个限制对不同网站差别很大有的站一分钟 30 次就封有的接口一分钟 1000 次也没事需要实测出自己的阈值。重试网络抖动和 429/5xx 错误都需要重试。我用的是指数退避初始 1 秒、每次翻倍、最多 5 次尝试间隔: 1s, 2s, 4s, 8s, 16s增量更新如果任务是周期性采集记录上次抓取的最大时间戳或 ID下次只拿变化数据。这比全量抓取省一半以上的请求量。这三件事组合起来你的数据通道才具备长期跑不崩的前提。团队里有位同事这样说过一句话我记录到今天爬虫不复杂难的是让爬虫明天还能跑、后天还能跑、下个月还健康。6. 二开路上最常踩的七个坑和对应解法几次完整项目跑下来我把最常见的坑整理成了一张排查表。每一个都花了不止一晚来填写出来希望你能直接用现象根因快速解法返回乱码页面编码不是 UTF-8请求后根据 headers 里的 charset 或 HTML meta 指定解码编码如response.encoding gbk请求一直被重定向缺少 follow_redirectshttpx.AsyncClient(follow_redirectsTrue)工具报错但 AI 无感知异常被吞返回空返回结构带error字段让模型读得到错误原因页面内容总是旧版本CDN 缓存请求头加Cache-Control: no-cache或给 URL 加时间戳参数无头浏览器跑完不退出异常路径未 closebrowser.close()必须放finally模型经常传错参数参数缺少描述把每个参数的意义和格式写进 docstringToken 经常超限抓取内容过长max_chars截断 优先提取正文6.1 页面编码乱码一个老问题的新坑现在大部分网站都是 UTF-8但老系统、部分政府服务和企业内部系统仍在用 GBK/GB2312。httpx默认按文本猜测解码猜错就乱码。正确做法是手动判定response await client.get(url) if response.headers.get(content-type, ).find(gbk) 0: response.encoding gbk更保险的是在 HTML 的meta charset里拿编码trafilatura内部其实已经做了这层处理但你直接读response.text就会踩坑。6.2 反爬机制UA、频率、验证码的应对思路反爬是网页抓取里绕不开的话题。我的原则是先遵守再优化绝不硬刚。合规的思路包括设置真实可辨识的 User-Agent而不是伪装成浏览器却发库默认头、控制请求频率避免给服务器造成压力、优先走官方公开 API、设置最大重试次数避免死循环。遇到验证码说明你的请求特征过于集中应该降频而不是想绕过方案。团队对公网资源的采集一律要求遵守目标站的 robots 协议和 ToS这是基本底线。6.3 会话失效导致工具静默失败这是AI 不知道自己在失败的典型案例。Cookie 过期后工具函数返回的听起来是正常数据实则是登录页 HTML。模型判断不了真假可能给出完全错误的分析。解法是在工具函数里加一道校验逻辑如果返回内容里出现请登录login等特征词就把状态标记为失败并提示需要刷新登录态。宁可让模型明确知道失败也不要让它拿到假数据而自信分析。6.4 超时设置不合理让 Agent 卡死控制好超时时间和重试次数。比如一个请求你设了 120 秒超时而模型调用后迟迟不返回整个 Agent 会看起来卡住体验极差。我的习惯是普通网页抓取 10~15 秒超时无头浏览器操作 30 秒API 请求 15 秒。同时配合重试把单次请求不可靠当默认前提而不是祈祷网络稳定。6.5 返回内容过长导致 token 爆炸不加以限制一个工具函数可以把 5 万字正文全部塞进上下文。LLM 的上下文窗口是有限且计费的必须设定输出上限。通用的做法是正文提取后只保留 max_chars 指定长度。更精细的方案是让extract先抽主干段落再按字数截断。模板参数化以后一次抓后可以反复消费性价比最高。6.6 工具异常时 LLM 会出现幻觉式编造结果机器也甩锅有时候模型会基于已有知识直接编出查询结果而不是承认工具调用失败。根治方法没有但有三道防线可以显著降低概率一是工具函数返回带明确的error字段和失败描述二是在工具描述里写明此结果必须来自真实接口不要猜测三是在 model 配置里把温度调低、把 max_tokens 适当放宽减少生成式添油加醋的冲动。6.7 并发调用时共享状态互相污染如果你的 OpenCLI 作为服务对外提供多个会话会并发调用同一个工具模块。模块级的全局变量缓存一旦被多会话共享就会出现A 用户查到的数据被 B 用户看到的严重问题。解决方法是会话级状态用独立上下文对象承载缓存只作为只读写入用临时目录或内存副本绝对不要共用可变全局变量。这个坑隐蔽且后果严重我专门安排过一次 code review 来扫整个工程的全局变量。7. 工程化落地从能跑到能长期跑个人玩具和团队基础设施的分水岭在于工程化。同样的工具代码做完下面这几件事稳定性天差地别。7.1 配置外置化URL、token、账号密码、Cookie 这些都不应该写进代码。我通常用一个settings.yaml或者环境变量统一管理代码里只留占位符。好处是不需要改代码就能在开发/测试/生产环境切换目标系统出问题时也能快速定位是不是某个配置项不对。# settings.yaml target: base_url: https://api.example.com auth_token: ${OPENCLI_TOKEN} request_interval: 1.5 tools: enabled: - web_reader - query_order7.2 日志与可观测性在工具函数关键节点加日志。不要等到出问题再回去加先加好再跑成本最低。一个建议为每次 AI 工具调用生成一个 trace_id让LLM 选了哪个工具、传了什么参数、工具返回了什么这三条信息能串联起来。出问题的时候你在日志里一搜就能看到完整决策链。我自己最大的效率提升就来自这个 trace_id 的设计没有它排查 AI 类问题就像在雾里找针。7.3 回归测试给工具函数写测试的必要性工具函数是纯函数式的天然好测。测试的建议很直白把真实网页内容存成 fixture 文件用 mock 的 HTTP 响应去测解析逻辑不要每次测试都真实访问外网。否则测试跑得慢不说目标站一改版你的测试就崩还分不清是代码挂了还是外部变了。我给工具函数的测试定了一个最低标准本地构造 3 个典型页面样本正文型、列表型、表格型解析结果必须可断言。7.4 扩展方向MCP 协议与多 Agent 协作完成上述工程化之后工具函数已经是一等公民能力。下一步的扩展方向我会优先推荐对接 MCPModel Context Protocol。MCP 本质上是把工具能力标准化让不同 Agent 客户端共享同一套工具服务端。对接之后OpenCLI 的工具可以同时被其他 MCP 客户端调用或者反过来接入别人写好的 MCP 服务生态一下子就打开了。在多 Agent 协作上一个重要经验是工具能力分开注册、会话上下文独立才能支持不同 Agent 各持所需。初步可以尝试主管 Agent 多个工具型 Agent的架构由主管负责拆解任务、分发给下面的工具 Agent。这样你写的工具函数就从一个对话里的伸手变成了一个组织里的员工复用率明显提高。我在实际项目里把订单查询、库存同步、内容采集三个工具都注册给了多个 Agent每个 Agent 挂了不同的职责文案和模型温度。运营同事提问时主管 Agent 会自动决定调用哪个工具人在对话里标关键词控制优先级。整套跑起来之后OpenCLI 从我个人的命令行助手变成了团队共享的 AI 服务总线这体验和单机玩具完全两个氛围。如果你的目标是让 AI 真正连接业务系统我的最终建议是从单一网站的最小读场景开始跑通工具注册回路再逐步升级到写操作和 API 直连。二次开发的投入比想象中可控但收益是确定性的——每次一个网站接入进来团队的 AI 就多了一只伸向真实世界的手。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

若依架构下AI功能落地:从单体到微服务的整合原理与实战 2026/9/30 10:35:14

若依架构下AI功能落地:从单体到微服务的整合原理与实战

1. 为什么“万物皆可若依”:先看懂平台架构的底层基因做了几年后台管理系统,我越来越觉得若依(RuoYi)这类脚手架最大的价值不是替你写代码,而是替你规范了“权限、组织、日志、监控”这些上一代人肉硬扛的脏活累活。到…

阅读更多 →
进程创建全解析:从fork到exec的内核底层原理 2026/9/30 10:35:07

进程创建全解析:从fork到exec的内核底层原理

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
Linux 离线安装 Git 三种方案:源码编译、离线包、便携二进制 2026/9/30 10:35:07

Linux 离线安装 Git 三种方案:源码编译、离线包、便携二进制

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
以太网组网实验核心解析:Hub与交换机冲突域对比及抓包验证 2026/9/30 10:35:07

以太网组网实验核心解析:Hub与交换机冲突域对比及抓包验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
软件设计的本质是决策,不是画图 2026/9/30 10:35:07

软件设计的本质是决策,不是画图

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
ViT代码逐行拆解:从Patch Embedding到Transformer Encoder 2026/9/30 10:35:07

ViT代码逐行拆解:从Patch Embedding到Transformer Encoder

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉