Devv 现在的定位: 截至 2026-10-21,devv.ai 首页和官方文档把 Devv 介绍为“用自然语言构建 AI 网页应用”的编程智能体(Devv Code),并内置数据库、登录、LLM 等集成;页脚的产品列表中仍有 Devv Search,
devv.ai/search也能打开。本组教程中,A、B 篇针对 Devv Search 这类“带来源的技术回答”,C 篇适用于向 Devv Code 粘贴报错信息的场景。
Devv Search 的回答会附上参考链接,但链接可能已经失效、版本不对,或根本没有提到回答使用的 API。 这篇做
cite_check.py:逐个打开引用链接,判断是不是官方文档、是哪个版本、页面是否提到回答中的 API、有没有弃用说明。
先看清成品
- 真实访问 3 个链接:
DataFrame.append的官方文档页 404(该方法在 pandas 2.0 删除);Stack Overflow 返回 403(拒绝脚本访问,不是死链)。- concat 页面含“Deprecated since version 3.0.0”,脚本附上前文,看出弃用的是
copy参数,不是 concat 本身。- 发现并修复:只报“页面含 Deprecated”会误导。pytest 4 项通过(离线)。
合规提醒:不要把公司私有代码、密钥、内部地址和用户数据粘贴给 Devv 或其他 AI 工具;引用 AI 给出的代码时注意开源许可证。本文只使用 Devv 官方网页功能,不调用非官方接口,不自动化操作 Devv 网站。额度和套餐以官网为准(本文不列价格)。
运行说明:box 上没有使用 Devv 账号,没有向 Devv 提问。示例“回答”(answer.md)是我按 AI 搜索回答常见的样子写的,不是 Devv 的真实输出。脚本和测试都在 box 上真实运行过(Python 3.13;pandas 3.0.6,另一个虚拟环境中为 pandas 2.2.3)。B 篇真实访问了回答中引用的 3 个链接。
做完之后你会得到什么?
| 成果 | 说明 |
|---|---|
cite_check.py |
检查引用链接 |
answer.md |
与 A 篇相同的示例回答 |
test_cite.py |
4 项离线测试 |
需要准备哪些环境?
| 项目 | 说明 |
|---|---|
| Python | 3.9 以上,pip install requests pytest pandas |
| Devv | 浏览器访问 devv.ai,登录账号(本文没有实际使用) |
| 第二个虚拟环境(可选) | A 篇用来对比不同库版本,例如 pip install "pandas==2.2.3" |
关键事实
| 事实 | 来源 |
|---|---|
pandas 2.0 删除了 DataFrame.append() |
pandas 2.0.0 发布说明 |
pandas 3.0 中 concat 的 copy 参数已被忽略并弃用 |
pandas.concat 官方文档(实测页面内容) |
文件结构
73-devv-B-src/
├── cite_check.py
├── answer.md
└── test_cite.py
第 1 步:脚本
"""检查 AI 回答中引用的链接:能否打开、是不是官方文档、文档是哪个版本、页面里是否提到回答所用的 API、有没有“deprecated / removed”字样。
链接打不开(404)往往说明 API 已被删除或改名;403/429 多半是网站拒绝脚本访问,要用浏览器手动看,不能直接当成死链。"""
import html, re, sys, time, requests
OFFICIAL = {"pandas.pydata.org": "pandas 官方文档", "docs.python.org": "Python 官方文档", "nextjs.org": "Next.js 官方文档",
"react.dev": "React 官方文档", "developer.mozilla.org": "MDN", "numpy.org": "NumPy 官方文档"}
UA = {"User-Agent": "binbho-tutorial/1.0 (citation check)"}
def links(text): return sorted(set(re.findall(r"https?://[^\s)>\]]+", text)))
def symbols(text):
"""回答正文中用反引号标出的 API 名(代码块之外)。"""
body = re.sub(r"```.*?```", "", text, flags=re.S)
return sorted({s for s in re.findall(r"`([A-Za-z_][\w.]*)`", body)})
def version_of(url):
m = re.search(r"/version/([\d.]+)/|/(\d+\.\d+)/", url)
return m.group(1) or m.group(2) if m else ("stable/最新版" if "/docs/" in url or "/stable/" in url else "")
def check(url, syms, get=requests.get):
host = re.sub(r"^www\.", "", url.split("/")[2]); kind = OFFICIAL.get(host, "第三方")
try: r = get(url, headers=UA, timeout=30, allow_redirects=True)
except requests.RequestException as e: return {"url": url, "kind": kind, "verdict": f"无法连接({type(e).__name__})"}
res = {"url": url, "kind": kind, "status": r.status_code, "version": version_of(url) if kind != "第三方" else ""}
if r.status_code == 404: res["verdict"] = "404:页面不存在(API 可能已删除或改名)"
elif r.status_code in (401, 403, 429): res["verdict"] = f"{r.status_code}:网站拒绝脚本访问,请用浏览器手动查看"
elif r.status_code >= 400: res["verdict"] = f"HTTP {r.status_code}"
else:
page = html.unescape(re.sub(r"\s+", " ", re.sub(r"<[^>]+>", " ", r.text)))
res["mentions"] = [s for s in syms if s.split(".")[-1] in page]
dep = re.search(r"[Dd]eprecated since version [\d.]+|has been removed|Deprecated", page)
# 只报“页面含 Deprecated”会误导:pandas.concat 页面中弃用的是 copy 参数,不是 concat 本身。所以附上前文
res["verdict"] = "可以打开" + (f";页面含弃用说明:“…{page[max(0, dep.start() - 140):dep.end()].strip()}”" if dep else "")
return res
def main(argv=None):
argv = argv or sys.argv[1:]; text = open(argv[0], encoding="utf-8").read(); syms = symbols(text)
print("回答中提到的 API:", ", ".join(syms) or "无"); bad = 0
for u in links(text):
r = check(u, syms); time.sleep(0.5)
bad += not r["verdict"].startswith("可以打开")
extra = f",版本 {r['version']}" if r.get("version") else ""
print(f"[{r['kind']}{extra}] {u}\n {r['verdict']}" + ((";页面提到 " + ", ".join(r["mentions"]) if r["mentions"] else ";页面未提到回答所用的 API") if "mentions" in r else ""))
print(f"{bad} 个链接需要人工处理"); return 1 if bad else 0
if __name__ == "__main__":
sys.exit(main())
第 2 步:测试
import cite_check as C
class R:
def __init__(self, code, text=""): self.status_code = code; self.text = text
def fake(table):
return lambda url, **k: table[url]
TEXT = open("answer.md", encoding="utf-8").read()
def test_extract():
assert len(C.links(TEXT)) == 3 and C.symbols(TEXT) == ["append"] # 代码块里的内容不算
def test_404_and_403():
g = fake({"https://x.org/a": R(404), "https://stackoverflow.com/q": R(403)})
assert C.check("https://x.org/a", [], g)["verdict"].startswith("404")
assert "浏览器手动查看" in C.check("https://stackoverflow.com/q", [], g)["verdict"]
def test_deprecation_context():
page = "<dt>copy</dt><dd>bool, default False. This keyword is now ignored. <p>Deprecated since version 3.0.0: will be removed</p></dd>"
r = C.check("https://pandas.pydata.org/docs/reference/api/pandas.concat.html", ["concat"], fake({"https://pandas.pydata.org/docs/reference/api/pandas.concat.html": R(200, page)}))
assert "copy bool" in r["verdict"] and r["kind"] == "pandas 官方文档" and r["version"] == "stable/最新版"
assert r["mentions"] == []
def test_versions():
assert C.version_of("https://pandas.pydata.org/pandas-docs/version/1.5/reference/x.html") == "1.5"
assert C.version_of("https://docs.python.org/3.12/library/re.html") == "3.12"
第 3 步:运行
$ python cite_check.py answer.md
回答中提到的 API: append
[pandas 官方文档,版本 stable/最新版] https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.append.html
404:页面不存在(API 可能已删除或改名)
[pandas 官方文档,版本 stable/最新版] https://pandas.pydata.org/docs/reference/api/pandas.concat.html
可以打开;页面含弃用说明:“…lways sorted lexicographically. copy bool, default False This keyword is now ignored; changing its value will have no impact on the method. Deprecated since version 3.0.0”;页面未提到回答所用的 API
[第三方] https://stackoverflow.com/questions/75956209/error-dataframe-object-has-no-attribute-append
403:网站拒绝脚本访问,请用浏览器手动查看
2 个链接需要人工处理
$ python -m pytest -q
4 passed
怎么检查结果?
- 404 的官方文档链接:去旧版本文档或发布说明中确认 API 是否已删除。
- 403/429:用浏览器打开看,不要直接当成死链。
- “页面未提到回答所用的 API”说明这个引用可能只是相关,而不是依据。
调试示例
弃用提示误导。 第一版发现页面中有 “Deprecated” 就提示“页面含 Deprecated since version 3.0.0”。concat 页面得到这个提示,看起来像 concat 也被弃用了,而 concat 正是 append 的官方替代方案。
我去掉 HTML 标签后查看上下文:
copy bool, default False This keyword is now ignored; changing its value will have no impact on the method. Deprecated since version 3.0.0: This keyword is ignored and will be removed in pandas 4.0.
被弃用的只是 copy 参数。修复:提示中附上弃用说明前面约 140 个字符(第一次取 90 个字符,正好截掉了 “copy”,又加长了一次)。
常见问题
| 现象 | 原因 | 处理 |
|---|---|---|
| 大量 403 | 网站拒绝脚本 | 手动打开;不要绕过网站的限制 |
| 官方域名没有被识别 | OFFICIAL 表中没有 | 补充你常用的文档域名 |
| 页面提到 API 但版本不对 | 引用的是旧版本文档 | 看 URL 中的版本号 |
模板规则
- 每个引用至少打开一次。
- 优先看官方文档,并确认版本。
- 403 不等于死链。
项目资源
| 用途 | 地址 |
|---|---|
| Devv 官网 | https://devv.ai/ |
| Devv Search | https://devv.ai/search |
| Devv 官方文档 | https://docs.devv.ai/introduction |
| pandas 2.0.0 发布说明 | https://pandas.pydata.org/docs/whatsnew/v2.0.0.html |
| pandas.concat 官方文档 | https://pandas.pydata.org/docs/reference/api/pandas.concat.html |
引用能打开,还要看它说的是不是你要的那件事。