把“差一点正确”救回来
小模型经常知道该调用什么,却输错协议形态。Forge 在客户端适配层完成救援解析,避免核心循环堆满模型家族特例。
云端前沿模型往往能容忍模糊提示与协议差异,8B 到 14B 的本地模型却会把每一个隐含假设放大成失败点。Forge 把这些失败点变成可检查的工程状态。
你仍然定义工具、业务逻辑和最终目标;模型仍然决定调用哪个工具以及调用顺序。Forge 负责把模型响应转换为可执行的结构,在真正执行前检查工具名与参数,并在错误时通过模型熟悉的消息通道反馈问题、触发重试。
需要严格流程时,可以给 WorkflowRunner 配置必经步骤、工具前置条件和终止工具;只想给现有 Coding Agent 或 OpenAI 兼容客户端加固时,则把 Proxy 放在客户端与本地推理服务之间。
这使 Forge 更像数据库驱动旁边的连接池、重试器与校验层:它不创造业务意图,但把原本脆弱的调用边界变得可预测、可观测、可失败。
模型把工具调用写进代码围栏、XML 或 Mistral 风格标记,而不是标准 tool_calls。
调用不存在的工具,或在应该使用工具时直接输出一段自然语言。
前置查询尚未完成,模型就尝试调用终止工具或依赖后续数据的工具。
长工作流把 KV Cache 推到显存边界,导致截断、降速甚至后端失败。
Runner 与 Proxy 的能力边界不同,但底层都复用同一组 Guardrails。错误不会被随意“修成一个默认值”,而是被识别、反馈、重新采样或显式终止。
保留调用方给出的 messages、tools、采样参数和认证信息。
在 native FC 与 prompt-injected 路径之间使用明确配置,不在运行时猜测切换。
从 fenced JSON、Mistral 标记、Qwen XML 等错误形态中提取结构化调用。
检查工具是否存在、参数是否为对象,并把错误送回规范通道。
在 Runner 中阻止过早终止和依赖未满足的调用,保持控制流独立于记忆。
成功后记录状态;预算耗尽则抛出类型化异常,保留最后响应与尝试次数。
小模型经常知道该调用什么,却输错协议形态。Forge 在客户端适配层完成救援解析,避免核心循环堆满模型家族特例。
未知工具、参数形态错误、前置条件失败会尽量走 role="tool" 的错误结果;裸文本失败则使用 user 角色重试提示。
Proxy 默认最多 3 次坏响应重试、2 次连续工具参数错误;Runner 还有总迭代上限与提前终止尝试上限。
步骤完成状态保存在 StepTracker。即使旧工具结果被压缩,流程约束仍然有效,不会因消息裁剪而“忘记规则”。
WorkflowRunner 可按优先级压缩上下文:先删除重试提示,再截断或删除旧工具结果,系统提示与原始用户输入始终保留。
reasoning_replay="none" 仍保留观测数据,但不把旧 reasoning 重新发给后端。项目评测认为它与完整回放质量接近、token 更省。
选择的关键不是“哪个最强”,而是你是否愿意让 Forge 接管完整循环。已有客户端通常从 Proxy 开始,新系统则更适合直接使用 WorkflowRunner。
它同时提供 OpenAI Chat Completions 与 Anthropic Messages 接口。把现有客户端的 API Base 指向 Forge,后端仍可以是 llama-server、Ollama、vLLM 或其他 OpenAI 兼容服务。
respond() 工具默认关闭。需要它时请显式添加 --inject-respond-tool。Proxy 的 SSE 也是完整推理结束后的格式化转发,不是 token 到达即透传。# 后端已在 8080 端口运行
python -m forge.proxy \
--backend-url http://localhost:8080 \
--port 8081
# 小模型需要强制在工具语法中选择“回答”时
python -m forge.proxy \
--backend-url http://localhost:8080 \
--port 8081 \
--inject-respond-tool
# 客户端统一指向
OPENAI_BASE_URL=http://localhost:8081/v1
适合新建 Agent、后台工作流或需要确定流程约束的系统。它负责消息构建、模型调用、工具执行、上下文预算、步骤状态、取消、流式观测和最终终止。
from forge import (
WorkflowRunner, ContextManager, TieredCompact
)
ctx = ContextManager(
strategy=TieredCompact(keep_recent=2),
budget_tokens=8192,
)
runner = WorkflowRunner(
client=client,
context_manager=ctx,
max_iterations=10,
max_retries_per_step=3,
reasoning_replay="none",
)
result = await runner.run(
workflow,
"完成这项需要多步工具调用的任务",
)
如果你已经有自己的事件循环、消息模型与工具执行器,可以只调用 check() 和 record()。Forge 返回 execute、retry、tool_error、step_blocked 或 fatal 等动作,由你的框架决定如何路由。
examples/foreign_loop.py 存在参数名漂移;StepEnforcer 的源码签名使用 terminal_tools。集成时应以源码签名为准。from forge import Guardrails
guard = Guardrails(
tool_names=["search", "summarize"],
required_steps=["search"],
terminal_tool="summarize",
)
checked = guard.check(model_response)
if checked.action == "execute":
results = execute(checked.tool_calls)
done = guard.record(
[(call.tool, call.args) for call in checked.tool_calls]
)
不同服务对工具协议、消息形态、模型身份和上下文预算的要求并不一致。Forge 用客户端适配层把这些差异隔离在核心循环之外。
| 后端 | 更适合 | 原生工具调用 | 关键说明 |
|---|---|---|---|
| llama-server | 性能、可控性与项目评测主路径 | 支持 | 使用支持 Function Calling 的模板并加 --jinja;项目推荐入口。 |
| Ollama | 最省事的本地模型安装与管理 | 支持 | 配置简单,但项目文档认为复杂工作负载通常略弱于 llama-server。 |
| Llamafile | 单文件分发与轻量部署 | Prompt 路径 | v0.8.1 重点修复 malformed 500 中工具调用的救援与失败隔离。 |
| vLLM | 高吞吐、AWQ/GPTQ 与服务化部署 | 支持 | 依赖服务端 parser;外部模式需要正确处理 served model name。 |
| Anthropic | 无本地 GPU、前沿模型基线或混合工作流 | 支持 | 需要可选依赖;当前正确导入路径是 forge.clients.anthropic。 |
| OpenAI-compatible | LM Studio、托管网关和其他兼容服务 | 取决于后端 | 外部 Proxy 可以直接接入;v0.8.0 增加单凭证校验与跨协议认证搬运。 |
Native FC 是当前 Proxy 默认路径。Prompt 模式只对 llama.cpp / Llamafile 这类缺少合适工具模板的后端显式开启,不会在请求中途自动探测切换。
Python 包名是 forge-guardrails,CLI 命令是 forge-proxy 或 python -m forge.proxy。核心依赖很轻,只有 Pydantic 与 HTTPX。
python3.12 -m venv .venv
source .venv/bin/activate
# 核心包
pip install forge-guardrails
# 需要 Anthropic 客户端时
pip install "forge-guardrails[anthropic]"
git clone https://github.com/antoinezambelli/forge.git
cd forge
python3.12 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
# 不需要真实 LLM 后端的单元测试
python -m pytest tests/unit -q
docker build -t forge-proxy .
docker run --rm -p 8081:8081 forge-proxy \
--host 0.0.0.0 \
--backend-url http://host.docker.internal:8000 \
--backend vllm \
--budget-mode manual \
--budget-tokens 8192
如果你的本地模型服务已经暴露 OpenAI 兼容接口,Forge 可以只做中间层。客户端仍然发送熟悉的 messages 和 tools,Forge 在返回前完成校验与重试。
--max-retries 3坏响应的连续重试预算。--max-tool-errors 2参数错误等工具通道错误预算。--backend-capability native默认,原样转发调用方的 tools。--reasoning-replay none默认不向后端回放历史推理。--budget-mode backend默认相信后端报告的上下文预算。--backend-api-keyv0.8.0 起可配置唯一静态后端凭证。# Forge 同时启动 llama-server 与代理
python -m forge.proxy \
--backend llamaserver \
--gguf /models/Ministral-3-8B-Instruct-Q8_0.gguf \
--port 8081
# 只把环境变量作用于当前 claude 进程
ANTHROPIC_BASE_URL=http://localhost:8081 \
ANTHROPIC_AUTH_TOKEN=anything \
claude
# OpenAI 兼容客户端则使用
OPENAI_BASE_URL=http://localhost:8081/v1
仓库提交了版本化 JSONL 数据、报告生成器和可交互 Dashboard。它让“哪个模型、哪个后端、哪组 Guardrail 有效”具备可复查的实验路径。
评测分为 OG-18 基础层和 8 个 advanced_reasoning 场景,包含工具选择、参数保真、多步顺序、条件路由、错误恢复、数据缺口恢复、参数变换和 grounded synthesis,并同时测试无状态与有状态版本。
仓库 Dashboard 还允许按 backend、native / prompt、模型家族、量化方式、推理回放策略和 ablation 配置筛选。报告不是外部独立基准,但其数据、脚本和生成视图都在仓库内。
docs/results/dashboard.html 在本地渲染。该视图生成时间为 2026-06-18。项目创建时间不长,但发布节奏活跃。最近 30 天有 3 个版本、12 个提交;更适合称为快速演进的 Beta 项目,而不是成熟生态。
llama.cpp 解析工具调用失败时,不再把原始 500 JSON 当作模型文本塞回对话。能救援的调用被恢复,不能救援的触发干净重采样,普通 500 则明确抛错。
增加 --backend-api-key,并在 OpenAI 与 Anthropic 协议之间搬运唯一凭证。零凭证和双凭证都会按规则明确失败,避免静默覆盖。
新增 none / keep-last / full,并用大规模评测比较。默认改为 none,保留观测但不重复消耗上下文回放历史 reasoning。
Forge 的价值来自边界清楚。正确使用时,它能显著减少协议和小模型行为的不确定性;放错位置时,则会让你误以为它应该承担编排、记忆或多模态能力。
| Proxy 上下文 | 单次请求内会做响应校验与重试,但跨请求消息窗口由客户端管理。不要宣称 Proxy 会替会话有效压缩历史。 |
|---|---|
| respond 工具 | 当前 CLI 与 ProxyServer 的 inject_respond_tool 默认都是 false。需要时显式使用 --inject-respond-tool。 |
| Anthropic 导入 | 当前 forge.clients 没有重新导出 AnthropicClient。源码使用应从 forge.clients.anthropic 导入。 |
| Middleware 示例 | 仓库示例仍传入旧参数 terminal_tool 给 StepEnforcer;当前类签名是 terminal_tools。文章示例改用更稳定的 Guardrails facade。 |
| 并行与流式 | 模型可以返回一批并行工具调用,但 WorkflowRunner 仍按批次顺序执行;Proxy 的 SSE 在后端完成推理后再转发,不是真正 token 级实时流式。 |
| 版本基线 | PyPI / Release 为 v0.8.1;本文审阅的 main 是 a02944a7,在 v0.8.1 之后多一个 Llamafile 修复提交。 |