中文 ▾
获取 API 密钥

Uncensored VideoSora 2 API:在视频管线中使用无审查文本

LangChain 智能体 AI 教程:使用 OpenAI 兼容 API

本 LangChain 智能体 AI 教程将 LangChain 连接至兼容 OpenAI 的接口,绑定两个工具,并运行可逐行阅读的智能体循环。每个代码片段均使用相同的 Base URL 和模型名称 uncensored。

更新于

机器手臂在工具立方体之间循环,展示 LangChain 智能体调用工具的过程

要点

  • ChatOpenAI 无需更改即可使用:设置 base_url、API 密钥和模型为 uncensored。
  • 该 API 支持函数调用,因此 bind_tools 和预构建的智能体均可正常工作。
  • 限制循环迭代次数并验证工具参数;开放权重模型有时会出错。
  • 在并行运行智能体时,请遵守完整密钥的 8 个并发请求限制。

开始前的准备工作

你需要 Python 3.10 或更高版本、API 密钥以及几个软件包。密钥来自 API 密钥页面:使用 Google 或电子邮件及密码注册,然后点击一次领取 $0.50 试用额度。该试用密钥允许 2 个并发请求和每分钟 60 个请求,如果一次只运行一个智能体,这足以满足本文的所有需求。

pip install langchain-openai langchain-core langgraph
export API_KEY="paste-your-key-here"

该 API 提供 POST /v1/chat/completions 和 GET /v1/models。LangChain 只需要第一个。该模型是一个单一的开放权重模型,经过调整以在法律允许的成人用途中无内容拒绝地回答问题;它由独立提供商运行,并非任何模型厂商的官方 API。文档列出了标头和限制,如果你想在编辑器旁查看它们。

各部分如何映射到 API

了解 LangChain 在传输层的行为很有帮助。HumanMessage 会变为 role 为 user 的消息。AIMessage 会变为 assistant,当它携带函数调用时,这些调用会作为 tool_calls 数组出现在助手消息中。ToolMessage 会变为 role 为 tool 并带有匹配的调用 ID。系统提示词会作为 role 为 system 的消息排在最前面。

这种映射对于调试很重要。如果智能体行为异常,请在每次调用前打印消息列表,并将其像转录文本一样阅读。你通常在一分钟内就能发现问题:工具结果为空、调用 ID 不匹配,或者历史记录变得比你预期的长得多。“智能体”一词可能让这听起来像魔法,但每一步都是一个普通的 chat completion 请求。

步骤 1:连接 ChatOpenAI

整个集成只需要三个参数。LangChain 构建标准的 OpenAI 风格请求,因此服务器无法区分差异。

import os
from langchain_openai import ChatOpenAI

llm = ChatOpenAI(
    model="uncensored",
    base_url="https://api.uncensoredvideo.cc/v1",
    api_key=os.environ["API_KEY"],
    temperature=0.7,
    max_retries=2,
)

print(llm.invoke("Name three beats of a heist scene, one line each.").content)

如果这打印了三行,说明连接正常。如果得到 401,请检查密钥。如果得到 404,请检查 URL 是否以 /v1 结尾且没有多余路径。max_retries 是一个客户端选项,有助于处理带有 Retry-After: 5 的临时 429 响应。

流式输出以你使用任何 OpenAI 模型时相同的方式工作:调用 llm.stream(...) 并迭代。最后一个块包含使用情况,因此你可以记录每次调用的 token 计数。

步骤 2:定义工具

智能体是一个循环:模型阅读对话,要么回答要么请求工具,你的代码运行工具,结果返回给模型。下面的工具是普通的 Python 函数。它们故意设计得很无聊,因为重点是展示机制。

from langchain_core.tools import tool

@tool
def word_count(text: str) -> int:
    """Count the words in a piece of text."""
    return len(text.split())

@tool
def spoken_minutes(words: int, words_per_minute: int = 150) -> float:
    """Estimate how many minutes a script takes to read aloud."""
    return round(words / words_per_minute, 2)

tools = [word_count, spoken_minutes]

文档字符串和类型提示变为模型看到的工具描述和 schema。将它们写得像在给同事交代任务。模糊的文档字符串是智能体选择错误工具的最常见原因。如果你为视频编写脚本,同样的模式可以扩展到查找镜头列表或检查长度预算的工具,该主题在 Sora 2 脚本生成器页面 上有介绍。

步骤 3:手动运行智能体循环

预构建的辅助函数隐藏了循环。自己编写一次会使以后的每个错误更容易找到。

from langchain_core.messages import HumanMessage, ToolMessage

by_name = {t.name: t for t in tools}
llm_tools = llm.bind_tools(tools)

messages = [HumanMessage(
    "Here is a draft: 'The vault door hums. Nina counts to three, "
    "then cuts the power.' How many words is it, and how long is it read aloud?"
)]

for step in range(6):
    ai = llm_tools.invoke(messages)
    messages.append(ai)
    if not ai.tool_calls:
        break
    for call in ai.tool_calls:
        result = by_name[call["name"]].invoke(call["args"])
        messages.append(ToolMessage(content=str(result), tool_call_id=call["id"]))

print(messages[-1].content)

三个细节很重要。range(6) 上限可防止模型困惑导致无限循环,同时也限制了你的支出。tool_call_id 必须与它回答的调用匹配。并且模型可以在一次回复中请求多个工具,这就是为什么内部循环处理列表的原因。

在底层,请求携带一个 tools 数组,回复携带 tool_calls,结果以带有 role: "tool" 的消息形式返回。你可以使用 tool_choice 强制特定工具,LangChain 将其作为参数暴露给 bind_tools。

步骤 4:使用预构建的 LangGraph 智能体

一旦手动循环变得清晰,预构建的智能体可以节省代码行数。它使用相同的模型和相同的工具。

import os
from langchain_openai import ChatOpenAI
from langgraph.prebuilt import create_react_agent

llm = ChatOpenAI(
    model="uncensored",
    base_url="https://api.uncensoredvideo.cc/v1",
    api_key=os.environ["API_KEY"],
)

agent = create_react_agent(llm, tools)
out = agent.invoke({"messages": [("user", "How long is a 450 word script read aloud?")]})
print(out["messages"][-1].content)

LangChain 和 LangGraph 在发布之间会更改其 API,一些辅助函数已被重命名或移动。如果导入失败,请检查当前的 LangChain 文档以获取等效的智能体构造函数。连接部分,即带有 Base URL 的 ChatOpenAI,保持不变。

在循环和图之间进行选择

手写循环足以处理从少量工具中选择并停止的智能体。它易于阅读、易于记录,且没有框架的意外行为。当你需要依赖于状态的分支时,请转向 LangGraph:例如将工作发回进行另一轮处理的审查步骤,在运行危险工具之前的人工批准,或多个将结果传递给彼此的专用子智能体。

无论你选择什么,保持每个节点小巧,并让它负责一项任务。输出简短编号列表作为 JSON 的规划节点、运行工具的执行节点以及生成最终文本的编写节点,比一个巨大的提示词更容易测试。它们还允许你使用不同的温度:规划和工具使用使用较低温度,编写步骤使用较高温度。由于每个节点都是正常的 chat completion 调用,相同的密钥、URL 和模型名称服务于所有节点,成本仅仅是调用的总和。

最后一个习惯:打印运行的 token 总数。在演示中看起来很便宜的智能体在混乱的真实任务中可能会花费十倍的费用,并且每个响应上的使用数据会在第一次运行时显示出来,以免在余额上出现意外。

记忆、测试和结构化输出

上面的循环在脚本结束时忘记所有内容。最简单的记忆是消息列表本身:在用户轮次之间保留它,追加每个新问题,并在变长时修剪最旧的消息。对于长会话,用模型编写的简短摘要替换旧消息。因为输入按 token 计费,500 token 的摘要在成本和注意力方面都优于 20,000 token 的原始历史。

测试智能体主要是检查你可以观察到的行为。将 temperature 设置为 0 或传递固定的 seed,API 会将其透传,从而使运行更具可重复性。然后编写一些你知道应该调用哪个工具的问题,并在消息列表中断言工具名称和参数,而不是断言答案的确切措辞。在每次更改提示词或工具描述后运行这些测试。更改文档字符串可能会改变模型选择的工具,而测试是发现这一点的最便宜方式。

为每次运行记录三件事:步骤数、带有其参数的工具调用以及 token 使用情况。当运行出错时,这三行告诉你模型是否选择错误、工具是否失败或循环是否耗尽步骤。

结构化输出和 JSON 模式

智能体经常需要返回数据,而不是文本。API 通过 response_format: {"type":"json_object"} 支持 JSON 模式。在 LangChain 中,你可以使用 llm.bind(response_format={"type": "json_object"}) 请求它。在提示词中告诉模型答案必须是 JSON 对象,并命名你期望的键,然后解析字符串并使用 Pydantic 验证它。不要相信解析步骤总是成功;将其包装在一个重试中,将错误文本发送回模型一次。

常见陷阱及修复方法

  • 工具参数错误。 开放权重模型有时会在你声明为整数的位置发送字符串。验证参数,从工具返回简短的错误消息,并让模型在下一步自行纠正。
  • 失控循环。 始终限制迭代次数。在开发期间记录每一步。
  • 并行 Agent。 完整密钥允许 8 个并发请求。如果你运行批处理,请在 llm.batch 上设置 config={"max_concurrency": 4} 以留出余量。超过限制会返回带有代码 concurrency 的 429;超过每分钟 300 个请求会返回 rate_limited。
  • 流式输出加工具。 流式请求会占用其并发槽位直到流结束,因此在试用密钥上不要在流处理程序内部启动新调用。
  • 增长的历史。 每一步都会重新发送完整的消息列表。上下文窗口为 100,000 token,但你需要为每次调用的输入付费。

智能体运行成本

考虑一个完成任务需要 4 次模型调用的适度智能体,平均提示词为 3,000 token,每次调用输出 200 token。输入:4 x 3,000 = 12,000 token,即 12,000 x $0.15 / 1,000,000 = $0.0018。输出:4 x 200 = 800 token,即 $0.0004。一次运行成本为 $0.0022,因此 1,000 次运行成本为 $2.20。$0.50 的试用额度可覆盖约 227 次此类运行。失败或被拒绝的请求不收费,且额度永不失效。查看 定价 了解充值金额和奖励。该网站仅接受加密货币,TRC20 上的 USDT 或 Base 上的 USDC。

问答

LangChain 需要为此 API 提供特殊集成吗?

不需要。langchain-openai 的 ChatOpenAI 类接受 base_url,因此你可以将其指向 API 并使用模型 uncensored。不需要自定义包装器。

API 支持智能体的函数调用吗?

支持。它接受 tools 和 tool_choice,返回 tool_calls(即使在流式输出时),并将结果以 role 为 tool 的消息形式返回。

为什么我的智能体以 429 错误停止?

你达到了请求速率或并发限制。完整密钥允许 8 个并发请求和每分钟 300 个请求。降低 max_concurrency 或等待 Retry-After 指定的时间。

我可以改用 LangGraph 而不是普通循环吗?

可以。LangGraph 的预构建智能体使用相同的 ChatOpenAI 对象和相同的工具列表,因此连接代码无需更改。

只差一张表单,即可获得密钥

创建账户,复制密钥,更改Base URL。这就是全部设置。

获取 API 密钥