LangChain Blog更新于

使用 ScaleDown 和 Deep Agents 构建长时间运行的智能体

LangChain发布langchain-scaledown集成,将ScaleDown针对单一任务训练的约束解码模型用于长时运行代理的上下文管理:三个中间件分别负责历史摘要、工具结果压缩和从用户消息提取结构化字段,并支持LangSmith LLM Gateway追踪。厂商自报基准称其比前沿模型更快更便宜,具体数字为其自身数据,且仅适用于窄任务。

The agent model runs the loop, and ScaleDown handles the context work around it: Summarize before each model call, Classify on the model’s output, Compress on tool results, and Extract on the user’s message.
配图来源 · LangChain Blog

要点

  • 构建长期运行的智能体需要持续的上下文管理,而使用前沿大语言模型来做出这些决策既缓慢又昂贵。ScaleDown 的受限解码模型(CDM)使其变得更快、更便宜。
  • langchain-scaledown 使其可以轻松地在你的 agent 中使用 ScaleDown 模型,并附带用于摘要、工具结果压缩和提取的中间件。
  • ScaleDown 现已在 LangSmith LLM Gateway 中受支持,因此其调用会被追踪,并计入您的消费额度和速率限制。

构建长时间运行的智能体归结为上下文管理的艺术:决定哪些信息进入上下文窗口,以及何时进入。这意味着要总结旧的历史记录、压缩大型工具结果,并从长输入中提取正确的数据。每一步都是一个决策。过去我们是用 LLM 来做这些决策的,但按照长时间运行智能体所需的规模,这样做既缓慢又昂贵。

为了让这些决策更快、更便宜,我们现在有一个 langchain-scaledown 集成与 LangSmith LLM 网关中的支持 为 ScaleDown的受限解码模型(CDM)。通用大语言模型逐个token生成任意字符串,而CDM只为单一任务训练,并且只返回该任务允许的答案,比如你列表中的标签或输入文本的片段。这使得它更小、更快、更便宜:ScaleDown报告称,在这些狭窄任务上,其模型比前沿模型快200倍、便宜467倍。

该集成自带三个 中间件 适用于 LangChain 和 Deep Agents:一个用于总结旧历史记录,一个用于压缩大型工具结果,还有一个用于从对话中提取结构化数据。在本文中,我们将介绍 ScaleDown 的模型是什么,以及如何使用它们来 缩减 你的代理的上下文。

ScaleDown 提供哪些模型?

ScaleDown 有四个模型,每个模型都针对单一任务进行训练,因此每个模型都比通用 LLM 高效得多:

  • Summarize 为长文本撰写简短摘要。
  • Compress 将一段上下文裁剪为与问题相关的部分。
  • Extract 从文本中提取你所描述的字段,每个字段都带有置信度分数及其在源文本中的位置。
  • Classify 将文本与你定义的标签进行比对打分,并为每个标签返回一个概率。你可以选择是否返回做出该分类的自然语言原因。
The agent model runs the loop, and ScaleDown handles the context work around it: Summarize before each model call, Classify on the model’s output, Compress on tool results, and Extract on the user’s message.
智能体模型运行循环,而 ScaleDown 处理其周围的上下文工作:在每次模型调用前进行 Summarize,对模型输出进行 Classify,对工具结果进行 Compress,并对用户消息进行 Extract。

每个模型最多可接受 1M tokens 的输入,因此一份完整合同、一个长政策页面或一小时的智能体历史记录都能在一次调用中容纳,无需分块。它们还是 多模态的:Extract、Summarize 和 Classify 除了非结构化文本外,还能直接接收图像和扫描的 PDF,因此截图或扫描的发票都可以作为输入。

在 ScaleDown 自己的基准测试中,与 GPT-5.5、GPT-5.4 Mini 和 GPT-5.4 Nano 的平均水平相比,在 公开数据集上测量,Summarize 便宜 41 倍、快 2.7 倍,Compress 便宜 138 倍、快 1.8 倍,同时两者在准确率上得分更高。

如果你用过像 Jev这样的决策模型,ScaleDown Classify 会让你感到熟悉:两者都返回带概率的类型化答案。Jev 每个问题仅接受 纯文本,最多 32K tokens 的状态。Classify 最多接受 1M tokens,包括图像,并且 为每个决策添加一句话的原因 。当智能体根据标签路由工单或停止运行时,这个原因能告诉你为什么,使智能体更易于调试。

摘要化 消息历史

长时间运行的智能体需要摘要,因为它们的历史最终会超出上下文窗口。一个已经运行了一小时的编码智能体,其历史中包含数十次文件读取和工具输出,其中大部分已不再需要逐字保留。每当历史填满时,用另一个前沿模型调用来摘要它们都会增加成本和延迟。

Once history crosses the trigger, ScaleDown replaces the older messages with one summary and the recent messages stay as is.
一旦历史超过触发点,ScaleDown 会用一份摘要替换较早的消息,而最近的消息保持原样。

ScaledownSummarizationMiddleware 替换了 Deep Agents 内置的 摘要中间件。它使用相同的 trigger 和 keep 选项来决定何时进行摘要以及保留哪些近期消息,而摘要由 ScaleDown 来编写:

from deepagents import create_deep_agent
from langchain_scaledown import ScaledownSummarizationMiddleware

agent = create_deep_agent(
    model="openai:gpt-6-sol",
    tools=[run_tests],
    middleware=[
        ScaledownSummarizationMiddleware(
            "openai:gpt-6-sol",  # the agent’s model, used for its context window
            trigger=("fraction", 0.8),
            keep=("fraction", 0.1),
        ),
    ],
)

Agent 保留自己的模型用于规划和编写代码。只有摘要转移到 ScaleDown,它默认返回与 LangChain 摘要提示词相同的部分(意图、摘要、产物和下一步)。如果 ScaleDown 调用失败,中间件会保持历史记录不变,agent 继续运行。同一中间件也适用于 LangChain 的 create_agent.

压缩 工具结果

长时间运行的 agent 会调用大量工具,而每个较大的结果都会在剩余运行期间一直保留在上下文中。一个抓取的网页可能长达数万字符,而 agent 只需要其中一段。后续每一步都要再次为这些 token 付费,并且随着其 上下文 被填满,agent 在保持任务专注方面会变得更差。

The model asks for what it needs on the tool call, and ScaleDown cuts the tool result down to the relevant parts before the model sees it.
模型在工具调用时说明自己需要什么,ScaleDown 会在模型看到之前把工具结果裁剪到相关部分。

ScaledownToolCompressionMiddleware 为你的工具添加一个可选的 compression_query 参数。当模型只需要结果的一部分时,它会在工具调用中填入 compression_query ,例如 "refund policy for EU orders"。中间件会在工具运行前将其剥离,然后 ScaleDown 会在模型看到之前把输出压缩到相关部分:

from deepagents import create_deep_agent
from langchain_scaledown import ScaledownToolCompressionMiddleware

agent = create_deep_agent(
    model="openai:gpt-6-luna",
    tools=[search_docs, fetch_url],
    middleware=[ScaledownToolCompressionMiddleware(tools=["fetch_url"])],
)

你的工具不需要改变,因此这适用于任何工具,包括 MCP 工具。在 这次追踪中,ScaleDown 在模型看到之前把一个很长的文档页面裁剪了将近 90%。压缩后的结果以类似下面这样的说明开头,因此模型在需要完整页面时可以不带 compression_query 再次调用该工具:

[Compressed by ScaleDown for "refund policy for EU orders":
 18,402 -> 3,110 chars.
 Call this tool again without `compression_query` for the full output.]

提取 结构化数据

Agent 需要不断地从所读取的内容中提取特定值,例如一个 ID、一个日期,或下一次工具调用所需的某个字段。这种提取位于 agent 循环内部,因此必须足够快以免给每一轮增加延迟,并且必须足够精确以便工具能够直接使用该值。

例如,在一个支持 agent 提交工单之前,它需要订单号、错误消息和客户的电子邮箱。主模型可以将这些填入工具参数,但它可能会转述错误代码,或敲错电子邮箱地址,而不是原样复制。

ScaledownExtractionMiddleware 把这一步交给 ScaleDown。你用自然语言描述每个字段,Extract 会从文本中复制该值。字段也可以是一组标签,ScaleDown 会用 Classify 来回答,这样工单就可以按问题类型进行路由。在一次运行的第一次模型调用之前,中间件会把用户的消息发送给 Extract,并将字段保存到 agent 状态中的 scaledown_extraction下,你的工具可以在那里读取它们:

from deepagents import create_deep_agent
from langchain.tools import ToolRuntime, tool
from langchain_scaledown import ScaledownExtractionMiddleware

ticket = ScaledownExtractionMiddleware(
    entities={
        "order_id": "Order number the customer mentions",
        "error_message": "Exact error message or code the customer saw",
        "customer_email": "Customer's email address",
        "issue_type": {
            "labels": [
                {"name": "billing", "rubric": "Is this about a charge, refund, or payment?"},
                {"name": "technical", "rubric": "Is this about a bug or something not working?"},
                {"name": "account", "rubric": "Is this about login, password, or account settings?"},
            ]
        },
    },
    context_chars=0,  # skip evidence snippets to keep state small
)


@tool
def file_ticket(runtime: ToolRuntime) -> str:
    """File a support ticket with the details gathered so far."""
    fields = runtime.state.get("scaledown_extraction", {}).get("fields", {})
    return crm.create_ticket(**fields)  # your ticketing system


agent = create_deep_agent(model="openai:gpt-6-sol", tools=[file_ticket], middleware=[ticket])

Extract 只读取用户的消息,从不读取 agent 的回复,因此每个值都是客户实际说过的话。With a checkpointer,提取操作每轮都会重新运行,因此随着客户补充细节,记录会逐步填充。对于 “订单 A-1042 支付失败,错误代码为 ERR_PAYMENT_DECLINED。我已经试过另一张卡了。”,各字段返回结果为:

{
    "order_id": "A-1042",
    "error_message": "ERR_PAYMENT_DECLINED",
    "customer_email": None,  # not mentioned yet
    "issue_type": "billing",
}

分类 漂移

智能体根据其当前状态决定下一步操作,例如任务是否已完成或上一次工具调用是否成功。对这种状态的分类每一轮都会进行,因此它必须廉价且快速。

在长时间运行的智能体中,一种值得捕获的状态是漂移:在同一个失败的工具调用上循环,或陷入无效的循环。对此没有预构建的中间件,但你可以在自己的 自定义中间件中调用任何 ScaleDown 模型。下面是一个断路器,它在每次模型调用后运行,将历史记录发送给 ScaleDown Classify,并在运行偏离轨道时停止运行:

import os

import requests
from deepagents import create_deep_agent
from langchain.agents.middleware import after_model
from langchain_core.messages import AIMessage, get_buffer_string

LABELS = [
    {"name": "progressing", "rubric": "Is the agent making new progress on the user's task?"},
    {"name": "looping", "rubric": "Is the agent repeating the same steps without new results?"},
    {"name": "drifting", "rubric": "Is the agent working on something the user did not ask for?"},
]


@after_model(can_jump_to=["end"])
def circuit_breaker(state, runtime):
    result = requests.post(
        "https://api.scaledown.xyz/classify",
        headers={"x-api-key": os.environ["SCALEDOWN_API_KEY"]},
        json={"text": get_buffer_string(state["messages"]), "labels": LABELS},
    ).json()

    label = result["top_label"]
    if label != "progressing" and result["scores"][label] > 0.8:
        return {"jump_to": "end", "messages": [AIMessage(f"Stopped: {result['reasoning']}")]}
    return None


agent = create_deep_agent(model="openai:gpt-6-sol", tools=[run_tests], middleware=[circuit_breaker])

Classify 会为每个标签返回一个概率,因此你可以选择断路器触发前所需的确信度。它的一行原因说明会显示在最终消息中,因此你可以看到运行停止的原因。一种更温和的版本不会在首次触发时结束运行:它将原因添加到对话中作为改变方向的提示,只有当智能体再次漂移时才停止。

这四个部分展示了 ScaleDown 模型适用的一些场景,而且这个列表并不详尽。你可以组合使用它们,例如对长的工具结果运行 extract / summarize,或使用分类进行运行时模型路由。请查看 ScaleDown Cookbooks 了解在不同类型的数据上使用 CDMs 模型的其他方式。

LangSmith LLM 网关中的 ScaleDown

该 LangSmith LLM 网关 让您只需一个 LangSmith API 密钥即可调用多家供应商的模型,追踪每一次调用,并强制执行您的 支出 和 速率限制. ScaleDown 现在是其 直接模型访问提供商, 在 /scaledown 路径。

管理员将您的 ScaleDown 密钥添加到工作区的 提供商机密 作为 SCALEDOWN_API_KEY 一次。然后指向 langchain-scaledown 该网关并使用你的 LangSmith 密钥进行身份验证:

export SCALEDOWN_BASE_URL="https://gateway.smith.langchain.com/scaledown"
export SCALEDOWN_API_KEY="$LANGSMITH_API_KEY"

每次 ScaleDown 调用随后都会显示为一条 LangSmith 跟踪记录 并计入与你其他模型调用相同的限额。

何时不使用 ScaleDown

对于开放式生成任务(如编写代码、起草回复和规划),请保留一个通用 LLM。这遵循了我们 Jev 文章中的“默认用廉价模型,例外用前沿模型”方法:将大型通用 LLM 用于困难部分,而将其周围的高吞吐量步骤交给专用模型。

ScaleDown 足够快速且廉价,可以在每一轮都运行,因此你的代理可以做出上下文管理所依赖的结构化决策,而不会拖慢主循环,也不必为这些决策支付前沿模型的价格。在一个长期运行的代理中,这些决策几乎在每一轮都会发生,因此节省的成本会迅速累积。

开始使用

安装该集成 并 设置你的 ScaleDown API 密钥:

uv pip install -U langchain-scaledown
export SCALEDOWN_API_KEY="..."

新 ScaleDown 账户可获得 50M 免费令牌。 ScaleDown 集成文档 涵盖了每个中间件的配置选项。试用一下 langchain-scaledown ,并在以下平台告诉我们你的想法: LangChain 论坛, X,或 GitHub.

原始出处

LangChain Blog

内容说明

原始发布及相关权利归来源方。

机器翻译 · 请以原文为准