跳转至

调用示例

供调用方参考。按场景提供完整可运行的代码,将 BASE 替换为实际地址后即可使用。

所有示例默认服务地址为 http://127.0.0.1:8864。示例中的 --noproxy '*' 用于绕过本机代理、直连内网服务;若当前环境未配置代理,可省略该参数。

完整问答

最常用的调用方式。CG_RAG 负责检索、重排及模型条文选取,调用方只接收最终结果。

curl --noproxy '*' -sS \
  -X POST http://127.0.0.1:8864/cg-rag/rag \
  -H 'Content-Type: application/json' \
  -d '{
    "query": "脚手架搭设完毕后,验收应当由谁组织?",
    "scope": "usual",
    "topk": 5,
    "max_items": 3
  }'
import requests

BASE = "http://127.0.0.1:8864"

resp = requests.post(
    f"{BASE}/cg-rag/rag",
    json={
        "query": "脚手架搭设完毕后,验收应当由谁组织?",
        "scope": "usual",
        "topk": 5,
        "max_items": 3,
    },
    timeout=180,
)
resp.raise_for_status()
body = resp.json()

generation = body["generation"]
if not generation.get("ok"):
    raise RuntimeError(generation.get("error"))

print(generation["answer_text"])
for item in generation["pred_items"]:
    print("-", item)
const BASE = "http://127.0.0.1:8864";

const resp = await fetch(`${BASE}/cg-rag/rag`, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    query: "脚手架搭设完毕后,验收应当由谁组织?",
    scope: "usual",
    topk: 5,
    max_items: 3,
  }),
});
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);

const { generation } = await resp.json();
if (!generation.ok) throw new Error(JSON.stringify(generation.error));

console.log(generation.answer_text);

响应中最重要的三个字段:

  • generation.answer_text:拼接完成的答案文本,可直接用于展示。
  • generation.pred_indices:模型选中的候选序号,1-based,对应 retrieval.retrieval_docs 中的位置。
  • generation.pred_items:按序号提取的条文,已格式化为「《规范名》第 X 条:正文」。

先看 generation.ok

HTTP 200 不代表生成成功。检索成功但模型未选出条文时,HTTP 仍返回 200,而 generation.ok 为 false、generation.error.code 为 no_stable_article。业务代码须判断 generation.ok,不可仅依赖 HTTP 状态码。

仅检索

不调用 LLM,响应速度最快,资源开销最低。适用于自行处理后处理逻辑,或将候选条文传入自有模型的场景。

curl --noproxy '*' -sS \
  -X POST http://127.0.0.1:8864/cg-rag/retrieve-rerank \
  -H 'Content-Type: application/json' \
  -d '{"query":"脚手架验收需要检查哪些条文?","scope":"usual","topk":10}'

返回的每条文档结构如下:

{
  "id": "17038",
  "law_name": "DB44-1876-2016轮扣式钢管脚手架安全技术规程",
  "article_number": "9.3.5",
  "contents": "《DB44-1876-2016轮扣式钢管脚手架安全技术规程》 模板支撑架搭设完毕后,应组织相关人员验收,验收合格后方可进入下一工序施工。",
  "text": "模板支撑架搭设完毕后,应组织相关人员验收,验收合格后方可进入下一工序施工。",
  "category": "standard_dibiao",
  "source": "bm25",
  "retrieval_score": 0.9409563541412354
}

source 表示该条文由哪一路召回(bm25 或稠密检索)。retrieval_score 为重排后的分数,已按降序排列。

相同的 query + scope + topk 组合在 TTL 内命中缓存时,响应中的 cache.hit 将为 true:

{ "cache": { "hit": false, "entries": 6, "hits": 7, "misses": 16, "ttl_seconds": 300.0 } }

对已有候选执行受约束生成

若已完成检索(或希望复用前一步的结果),可单独调用受约束生成。

import requests

BASE = "http://127.0.0.1:8864"
query = "脚手架搭设完毕后,验收应当由谁组织?"

# 1. 先检索
docs = requests.post(
    f"{BASE}/cg-rag/retrieve-rerank",
    json={"query": query, "scope": "usual", "topk": 10},
    timeout=60,
).json()["retrieval_docs"]

# 2. 再让模型从这批候选里挑
result = requests.post(
    f"{BASE}/cg-rag/constrained-generate",
    json={"query": query, "retrieval_docs": docs, "max_items": 3},
    timeout=180,
).json()

print(result["answer_text"])
print("选中序号:", result["pred_indices"])

retrieval_docs 中每项至少须包含 law_name、article_number、contents,其余字段将被忽略。超出服务端 max_context_docs(默认 78)的部分将被截断。

边生成边显示(SSE)

/cg-rag/rag/stream 通过 Server-Sent Events 返回过程事件,适用于前端实现逐字输出效果。

import json
import requests

BASE = "http://127.0.0.1:8864"

with requests.post(
    f"{BASE}/cg-rag/rag/stream",
    json={"query": "脚手架搭设完毕后,验收应当由谁组织?", "scope": "usual", "topk": 5},
    headers={"Accept": "text/event-stream"},
    stream=True,
    timeout=180,
) as resp:
    resp.raise_for_status()
    event = None
    for line in resp.iter_lines(decode_unicode=True):
        if not line:
            continue
        if line.startswith("event:"):
            event = line[6:].strip()
        elif line.startswith("data:"):
            payload = json.loads(line[5:].strip())
            if event == "token":
                print(payload.get("text", ""), end="", flush=True)
            elif event == "final":
                print("\n完成:", payload["generation"]["ok"])
            elif event == "error":
                print("\n出错:", payload)

事件顺序为 progress → retrieval → token* → final;出错时为 error 后紧跟一个 final。完整语义见 HTTP 参考 · SSE。

受约束生成的「流式」是重放,不是真流

受约束生成的答案由服务端按模型选中的序号拼接生成,并非模型逐字输出。服务端在拼接完成后将其切分为若干 token 事件,按 stream_answer_token_delay_seconds(默认 0.025 秒)依次发出,以使前端获得打字机效果,同时保持与其他模式一致的渲染路径。

但须明确:这不会使答案更早到达。第一个 token 到达时,生成实际上已经结束。真正边生成边到达的是视觉接口和自由生成。

图片隐患识别

图片以 base64 编码传输,不使用 multipart。

import base64
import requests

BASE = "http://127.0.0.1:8864"

with open("site.jpg", "rb") as fh:
    encoded = base64.b64encode(fh.read()).decode("ascii")

payload = {
    "question": "请检测图中的安全隐患",
    "images": [{"data": encoded, "mime_type": "image/jpeg"}],
}

# 自由生成:只看图,快,不给法规引用
fast = requests.post(f"{BASE}/cg-rag/vision/answer", json=payload, timeout=180).json()
print(fast["answer"])

# 智能生成:模型自己去检索法规,答案里带 [cite^n] 引用
deep = requests.post(
    f"{BASE}/cg-rag/vision/agentic",
    json={**payload, "scope": "full", "topk": 8},
    timeout=600,
).json()
print(deep["answer"])
for citation in deep.get("citations", []):
    print(citation)

两种模式的差异对比:

自由生成 vision/answer 智能生成 vision/agentic
是否检索法规 否 是,模型自行决定检索轮次
答案里有无引用 无 有 [cite^n],可追溯条文
典型耗时 数秒 一到三分钟
适合 现场快速核查 出具有依据的判定

智能生成耗时较长,原因在于其执行多轮检索。若场景要求低延迟响应,应使用自由生成。

字段全集及隐患定级口径见 视觉。

通过 MCP 调用

CG_RAG 通过 Streamable HTTP 暴露 MCP,地址为 http://127.0.0.1:8864/cg-rag/mcp/(末尾斜杠不可省略)。

Claude Code:

claude mcp add --transport http cg-rag http://127.0.0.1:8864/cg-rag/mcp/

其他客户端的通用配置:

{
  "mcpServers": {
    "cg-rag": {
      "type": "http",
      "url": "http://127.0.0.1:8864/cg-rag/mcp/"
    }
  }
}

可用工具:cg_health、cg_list_profiles、cg_retrieve_rerank、cg_constrained_generate、cg_rag、cg_vision_answer、cg_vision_observe、cg_vision_agentic。各工具参数与 HTTP 接口一一对应,详见 MCP 工具。

让 CG_RAG 开机就热

首次请求某个 scope 时需加载索引,响应延迟会明显升高。上线后应提前执行 warmup:

curl --noproxy '*' -sS \
  -X POST http://127.0.0.1:8864/cg-rag/warmup \
  -H 'Content-Type: application/json' \
  -d '{"scopes":["usual","full"]}'

注意:retriever 同时仅保留一个 active profile,warmup 将按顺序加载,最终停留在数组中最后一个 scope。应将最常用的 scope 置于数组末尾。

客户端超时建议

建议按接口分别配置,不宜统一设定:

接口 建议客户端超时
/cg-rag/health、/cg-rag/profiles 10 s
/cg-rag/retrieve-rerank 60 s(首次加载索引可能耗时较长)
/cg-rag/constrained-generate、/cg-rag/rag 180 s
/cg-rag/vision/answer 180 s
/cg-rag/vision/agentic 600 s,该接口执行多轮检索
/cg-rag/vision/hybrid 600 s,图片分析后继续执行检索

服务端自身的上游超时由 CG_RAG_TIMEOUT_SECONDS 控制,默认 120 秒(生产部署通常调整为 180 秒)。客户端超时不应小于该值,否则客户端将先于服务端断开连接,日志中仅留下断连记录。当前生效值可在 health 响应的 generation.timeout_seconds 字段中查看。