如何运行 先把下方完整代码保存为 rag_contract.py,再选择与当前环境相符的运行方式;普通学习者访问公开 HTTPS API,本地开发者才需要显式改用内部开发端口。
普通用户默认运行:通过公开 HTTPS API 检查 Chunk → Index → Retrieval → Context
python .\rag_contract.py 部署此版本后,脚本默认请求 https://algolab.coding-x.tech/api/rag。Nginx 会把 /api 请求转给服务器内部 Backend;用户无需开放或访问 4000 端口,也无需启动 Local Runner。
项目开发者本地联调:显式覆盖为 localhost Backend(PowerShell)
$env:ALGOLAB_API_URL = 'http://127.0.0.1:4000/api/rag'
python .\rag_contract.py 仅在本机已经启动 AlgoLab Backend 时使用。4000 是开发/服务器内部端口,不是部署网站要求普通用户访问的地址。
普通用户可选:公开 API 检索后,直接调用自己电脑上的 Qwen / LoRA(PowerShell)
$env:ALGOLAB_MODEL_PATH = 'D:\models\Qwen2.5-1.5B-Instruct'
$env:ALGOLAB_ADAPTER_PATH = 'D:\models\algolab-lora-adapter' # 没有 adapter 可留空
$env:ALGOLAB_LOCAL_RUNNER_URL = 'http://127.0.0.1:4890'
python .\rag_contract.py 先在用户电脑启动 Local Runner。前三步访问公开 HTTPS API;第四步由该 Python 进程直接请求本机 4890,不要求生产服务器反向访问用户电脑。
复制 import json
import os
from urllib.error import HTTPError, URLError
from urllib.request import Request, urlopen
API_BASE = os.getenv ("ALGOLAB_API_URL" , "https://algolab.coding-x.tech/api/rag" ).rstrip ("/" )
LOCAL_RUNNER_URL = os.getenv ("ALGOLAB_LOCAL_RUNNER_URL" , "http://127.0.0.1:4890" ).rstrip ("/" )
MODEL_PATH = os.getenv ("ALGOLAB_MODEL_PATH" , "" ).strip ()
ADAPTER_PATH = os.getenv ("ALGOLAB_ADAPTER_PATH" , "" ).strip ()
QUERY = "为什么 Tool Calling 不能只看 loss?"
def post_json (base_url, path, payload, timeout=600 , service_name="AlgoLab RAG API" ):
request = Request (
base_url + path,
data=json.dumps (payload, ensure_ascii=False ).encode ("utf-8" ),
headers={"Content-Type" : "application/json; charset=utf-8" },
method="POST" ,
)
try :
with urlopen (request, timeout=timeout) as response:
return json.loads (response.read ().decode ("utf-8" ))
except HTTPError as exc:
detail = exc.read ().decode ("utf-8" , errors="replace" )
raise RuntimeError (f"{service_name} {path} 返回 HTTP {exc.code}: {detail}" ) from exc
except URLError as exc:
raise RuntimeError (
f"无法连接 {service_name} {base_url}。原始错误:{exc.reason}"
) from exc
def print_json (title, value):
print (f"\n=== {title} ===" )
print (json.dumps (value, ensure_ascii=False , indent=2 ))
def main ():
print ("AlgoLab RAG API:" , API_BASE)
print ("Default retrieval: Backend CPU hash-ngram exact baseline" )
chunk_result = post_json (API_BASE, "/chunk" , {
"modules_from" : 8 ,
"modules_to" : 8 ,
"chunk_size" : 520 ,
"overlap" : 80 ,
"sample_limit" : 3 ,
})
print_json ("1. real Backend CPU chunking" , chunk_result)
assert chunk_result.get ("ok" ) is True
assert chunk_result.get ("execution" , {}).get ("device" ) == "cpu"
assert int (chunk_result.get ("chunk_count" , 0 )) > 0
index_result = post_json (API_BASE, "/index" , {
"modules_from" : 1 ,
"modules_to" : 13 ,
"chunk_size" : 520 ,
"overlap" : 80 ,
})
print_json ("2. real Backend CPU index" , index_result)
assert index_result.get ("ok" ) is True
assert index_result.get ("index_id" )
assert index_result.get ("embedding_backend" ) == "hash-ngram-fnv1a64-v1 (lexical baseline)"
retrieval_result = post_json (API_BASE, "/retrieve" , {
"index_id" : index_result["index_id" ],
"query" : QUERY,
"top_k" : 4 ,
"max_per_module" : 3 ,
"context_budget" : 2200 ,
})
print_json ("3. real Backend CPU retrieval + context" , retrieval_result)
hits = retrieval_result.get ("hits" ) or []
citations = retrieval_result.get ("citations" ) or []
assert retrieval_result.get ("ok" ) is True
assert retrieval_result.get ("index_id" ) == index_result.get ("index_id" )
assert hits and citations and retrieval_result.get ("context" )
assert int (retrieval_result.get ("context_used" , 0 )) <= int (retrieval_result.get ("context_budget" , 0 ))
print ("\nTop-K calculation trace:" )
for hit in hits:
print (
f" {hit.get('id')} rank={hit.get('rank')} score={hit.get('score'):.6f} "
f"No.{hit.get('module_no')} · {hit.get('section_title')} "
f"offset={hit.get('start')}:{hit.get('end')}"
)
if not MODEL_PATH:
print (
"\n4. generation not run: 未设置 ALGOLAB_MODEL_PATH。"
"前三步已在 Backend CPU 上真实完成,不需要 Local Runner。"
)
return
generation_request = {
"coordinator" : "standalone_rag_contract" ,
"query" : QUERY,
"context" : retrieval_result["context" ],
"citations" : citations,
"model_path" : MODEL_PATH,
"adapter_path" : ADAPTER_PATH,
"max_new_tokens" : 420 ,
"temperature" : 0.2 ,
"compare_without_rag" : False ,
"index_id" : index_result["index_id" ],
"embedding_backend" : retrieval_result.get ("embedding_backend" ),
"retrieval_ms" : retrieval_result.get ("retrieval_ms" ),
"context_used" : retrieval_result.get ("context_used" ),
"context_budget" : retrieval_result.get ("context_budget" ),
"context_remaining" : retrieval_result.get ("context_remaining" ),
"context_budget_unit" : retrieval_result.get ("context_budget_unit" ),
"budget_measurement" : retrieval_result.get ("budget_measurement" ),
"budget_trace" : retrieval_result.get ("budget_trace" ),
}
answer_result = post_json (
LOCAL_RUNNER_URL, "/rag/generate" , generation_request,
timeout=1800 , service_name="Local Runner" ,
)
print_json ("4. real Local Qwen / LoRA generation" , answer_result)
assert answer_result.get ("ok" ) is True
assert answer_result.get ("context" ) == retrieval_result.get ("context" )
assert str (answer_result.get ("answer" ) or "" ).strip ()
assert isinstance (answer_result.get ("valid_references" ), list)
print ("\nchecks passed: CPU retrieval 与按需本地生成共享同一份固定 context" )
if __name__ == "__main__" :
main ()