mcpField
L
langgraph-decision-models

langgraph-decision-models

github.com/langchain-aiAgent 工作流1.3k1.8k 次安装3 天前更新
代码仓库

关于 langgraph-decision-models

langgraph-decision-models 是面向 LangGraph 的 LangChain 技能,用带类型的概率分类替代生成文本后再解析决策。适合构建路由、分流、过滤、护栏和批量分类节点的开发者。

主要功能

  1. 01输出二元判断概率
  2. 02按标签选择并返回分布
  3. 03按评分标准计算分数
  4. 04单次请求处理最多 32 个问题
  5. 05提供 LangGraph 路由与批处理指导

适用场景

  1. 01按账单相关性路由工单
  2. 02筛选需要人工审核的条目
  3. 03依据语义标准过滤内容
  4. 04对批次中的每个项目分类

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/langchain-ai/langchain-skills --skill langgraph-decision-models

安装后在 Claude Code 中输入/langgraph-decision-models确认已加载。

附带文件references/conversion-playbook.md

详细介绍

**决策模型**针对状态回答类型化问题,并返回概率,而非散文。它取代了常见做法:向大模型发送提示词、解析其文本,再根据结果进行分支处理。
  • Noul(instructions=...) — 二元问题,返回答案为“是”的概率
  • Choice(instructions=..., criteria={...}) — 选择一个标签,返回完整的概率分布及置信度
  • Score(instructions=..., criteria=[...]) — 按有序评分标准打分,返回期望值及置信度

TypeSafeClassifier 是 LangChain 的 Runnable[ClassifierRequest, ClassifierResponse],因此可像其他 runnable 一样直接用作节点。一次请求最多可包含 32 个问题,且会分别回答——由你的代码组合这些结果。

适合使用的场景: 节点只生成文本,而你只是想从中解析出一个决策,例如路由、分诊、过滤、护栏,或对批次中的每个项目进行分类。

不适合使用的场景: 节点的输出本身就是最终产物(摘要、草稿、代码),或判断需要多步推理。决策模型负责分类,不负责思考。


安装和接入

langchain-typesafe 仍处于 alpha 阶段(0.0.1a3),且 TypeSafeClassifier 标记为 @beta——请固定版本,并做好接口变动的准备。

uv add langchain-typesafe

有三种方式可连接模型。分类器会向 {base_url}/v1/systemone 发送 POST 请求,并附带 Authorization: Bearer {api_key},因此切换提供商只需修改构造参数:

```python import os from langchain_typesafe import TypeSafeClassifier

1. TypeSafe directly (Jev). Reads TYPESAFE_API_KEY when api_key is omitted.

classifier = TypeSafeClassifier(model="jev-latest")

2. SemIf, hosted on the LangSmith Gateway. Note: LangSmith key, not a TypeSafe key.

classifier = TypeSafeClassifier( model="semif-qwen3.5-4b", api_key=os.environ["LANGSMITH_API_KEY"], base_url="https://gateway.smith.langchain.com", )

3. Jev through the Gateway (BYOK). The typesafe/ prefix routes to a

TYPESAFE_API_KEY stored in LangSmith workspace secrets. Without that secret

every typesafe/* id returns 424 Failed Dependency -- before the model name is

even validated, so a 424 does not confirm the id is real.

classifier = TypeSafeClassifier( model="typesafe/jev-1.13.0", api_key=os.environ["LANGSMITH_API_KEY"], base_url="https://gateway.smith.langchain.com", )

</python>
</ex-wiring>

---

## 转换模式

在**一次**请求中询问有关某个页面/项目的所有问题,将类型化响应放入状态,再用普通函数进行路由。路由器是普通的 Python 代码,无需访问网络即可测试。

<ex-classify-and-route>
<python>
```python
from typing import TypedDict
from langchain_typesafe import ClassifierResponse, Noul, Score, TypeSafeClassifier
from langgraph.graph import StateGraph, START, END

QUESTIONS = {
    "relevant": Score(
        instructions="How relevant is this ticket to a billing problem?",
        criteria=["Unrelated.", "Possibly related.", "Directly about billing."],
    ),
    "angry": Noul(instructions="Is the customer expressing anger?"),
}

class State(TypedDict):
    text: str
    answers: ClassifierResponse
    route: str

classifier = TypeSafeClassifier(model="jev-latest")

def classify(state: State) -> dict:
    # One request, every question. They are answered independently.
    return {"answers": classifier.invoke(
        {"state": state["text"], "questions": QUESTIONS}
    )}

def route(state: State) -> str:
    a = state["answers"]
    if a.nouls["angry"].noul > 0.7:
        return "escalate"
    if a.scores["relevant"].score < 0.5:
        return "close"
    return "handle"

读取答案:response.nouls[id].noul、response.choices[id].choice、response.scores[id].score。每个视图都以你的问题 ID 为键;response.answers 则包含所有答案。


读取答案时的三个陷阱

这些问题会导致静默误路由,而不会抛出异常。

1. Score.score 是期望值,不是等级。 它是按概率对评分标准加权后得到的平均值,通常会有小数。score == 0 几乎不会成立——“不相关”的项目会得到 0.07,而不是 0。应始终与一个区间比较。

if a.scores["relevant"].score < 0.5:   # correct
if a.scores["relevant"].score == 0:    # WRONG -- nearly never true

2. 置信度衡量的是分布形态,而非正确性。 对于 Score,置信度表示评分标准的分布有多集中。如果某个项目恰好处于两个等级之间,即使模型对此判断十分明确,置信度也会较低。因此,一律按 confidence < X -> escalate 处理,会把模型已经判定的项目也升级处理。仅在存在歧义的中间区间内根据置信度设门槛:

if score < NOT_RELEVANT:                          # decisive -- trust it
    return "close"
if score < RELEVANT or confidence < MIN_CONF:     # ambiguous -- escalate
    return "human_review"
return "handle"

3. 阈值不能在不同模型间沿用。 校准是模型的一部分。同一策略应用于同一批项目,在 Jev、SemIf 和 LLM 适配器上的路由结果会有所不同。每次更换模型时都要重新调整阈值,并固定模型 ID。


问题措辞对准确率影响最大

宽泛的问题会产生自信却错误的答案,任何阈值都无法弥补。使用 criteria 说明每种结果的含义,包括哪些内容不应计入。

在一个实测案例中,问题“这是否为与律师之间的保密通信?”将一份常规财务备忘录评为 0.798。将问题改写为明确指出实际判定标准——由律师撰写或发送给律师,并明确排除财务和会计内容——同一页面的分数便降至 0.005,而一份确实享有法律特权的页面则保持在 0.991。

Noul(
    instructions=(
        "Was this written by or to a lawyer, or does it convey a lawyer's legal "
        "advice? Answer no for ordinary business or accounting discussion, even "
        "when the subject is litigation-sensitive."
    ),
    criteria=NoulCriteria(
        true="A named attorney is author or recipient, or it relays legal advice.",
        false="Business or accounting content with no attorney involved.",
    ),
)

在归咎于模型之前,先改写问题并重新测量。


审查现有 Agent

要找到决策模型适用的位置,请在代码库中查找以下内容——完整指南请参阅 references/conversion-playbook.md。

信号查找内容
生成后解析LLM 调用的输出会立即经过正则表达式处理、json.loads 解析,或字符串匹配后进入某个分支
提示词分类器提示词中包含“从以下选项中回复一个”“回答是或否”“按 1 到 5 评分”等内容
为降低成本而抽样注释或配置表明只检查前 N 个项目,因为检查全部项目成本太高
脆弱的规则用关键词列表或正则表达式代替语义判断
重复读取上下文针对每个单独的问题,都将同一份文档重新发送给模型

最后两项最重要:低成本的语义判断改变的是你能构建什么,而不仅仅是费用。如果评估每个项目都变得经济可行,你会停止抽查什么?


预期表现

在包含 24 个项目的批次上测得;LangGraph 图和路由策略完全相同,仅替换了分类器:

每个项目tokens(6 个项目)备注
Jev 1.13.00.27s3,648 输入 / 318 输出会报告用量
SemIf 4B0.49s未报告托管在 Gateway 上
Claude Sonnet 52.87s7,930 输入 / 864 输出通过结构化输出

不同引擎在 6 个项目中有 4 到 5 个项目的路由结果一致;不一致主要集中在确实难以判定的项目上。请将这些数据视为大致情况,而非基准测试——请在自己的工作负载上进行测量。

如果要与 LLM 基线进行比较,请使用 method="json_schema",以确保比较公平。LangChain 的 with_structured_output 默认使用 method="function_calling",这会在每个请求中注入工具架构——对于同一个单字段探测,原生 output_config.format 路径需要 228/12 个 tokens,而该方式需要 556/35 个。


使用 Send 进行批处理

分类按项目进行,且彼此独立,因此可以使用 Send 扇出,让每个项目各自进行路由。

```python from langgraph.types import Send

def fan_out(state): return [Send("classify_item", {"text": t}) for t in state["items"]]

builder.add_conditional_edges(START, fan_out, ["classify_item"])

</python>
</ex-fan-out>

扇出可以隐藏延迟,因此对慢速分类器最有利:在上述运行中,Sonnet 因并发获得了 8 倍提升,Jev 仅提升 1.4 倍——但 Jev 仍然最先完成。请比较吞吐量,而非加速倍数。

---

## 相关技能

- **langgraph-fundamentals** — StateGraph、`Send`、`Command`、条件边
- **langgraph-human-in-the-loop** — 使用 `interrupt()` 处理上述升级分支
- **langchain-middleware** — 需要使用 LLM 而非分类器时使用结构化输出

评论

支持 Markdown