ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Hindsight:轻量级LLM请求回溯系统,专治API调试盲区

2026/10/2 5:02:10 拓冰建站 浏览量
Hindsight:轻量级LLM请求回溯系统,专治API调试盲区 1. 项目概述Hindsight 不是“事后诸葛亮”而是一套可落地的 LLM 操作回溯系统你有没有遇到过这样的情况调用 OpenAI API 时突然返回401 Unauthorized: incorrect api key provided但你刚确认过 key 是对的或者模型突然报错400 This models maximum context length is 1048576 tokens可你压根没传那么长的文本又或者 Docker 容器里服务明明启动了curl 却连不上日志里只有一行connection refused—— 这些问题不是代码写错了而是你看不见请求发出去那一刻的真实状态。Hindsight 就是为解决这个盲区而生的它不是个新模型也不是个 API 网关而是一个轻量、可嵌入、带完整上下文捕获能力的 LLM 请求观测层。它的核心价值在于——把“黑盒调用”变成“白盒操作”让每一次openai.ChatCompletion.create()调用都像在示波器上看到电压波形一样清晰可查。关键词hindsight在这里不是哲学概念而是技术代号它指代一套运行在本地或容器内的中间件自动拦截、记录、结构化、可视化 LLM 请求与响应的全链路数据包括原始 payload、headers、timestamp、耗时、token 统计、错误堆栈甚至能还原出被截断的 prompt 片段。它不替换你的现有代码也不强制你改用某套 SDK而是以最小侵入方式比如一行import hindsight 一个装饰器接入 Python、Node.js 或任何支持 HTTP 拦截的环境。适合三类人正在调试 API 集成的后端工程师、需要复现用户报错的 SaaS 产品支持团队、以及想搞清 token 消耗到底卡在哪一步的 Prompt 工程师。它解决的不是“能不能用”而是“为什么这么用”和“刚才到底发生了什么”。2. 设计思路拆解为什么 Hindsight 必须绕开传统日志与代理方案很多人第一反应是“不就是打日志吗我加个logging.info(freq: {payload})不就完了”——这恰恰是 Hindsight 要规避的第一个坑。传统日志方案在 LLM 场景下有三个致命缺陷一是信息失真比如你代码里传的是messages[{role: user, content: long_text}]但日志里只打印出str object at 0x...根本看不到实际内容二是上下文割裂一次完整对话可能跨多个 API 调用比如先 summary 再 rewrite 再 translate日志分散在不同时间戳、不同线程里人工拼凑成本极高三是无法捕获网络层真相日志记录的是你“想发什么”但真实发出的 HTTP 请求可能被 SDK 自动加了 header、重写了 content-type、甚至做了 chunked encoding而这些细节恰恰是401和400错误的根源。另一个常见方案是用 mitmproxy 或 Charles 做全局抓包但这又引入了第二个维度的问题环境污染与不可复现性。你在本地装了 mitmproxy证书配置一通结果发现 Docker 容器里跑的服务根本不走 host 的代理链路或者你在 Windows 上用 Fiddler但同事 Mac 上跑不起来更麻烦的是这类工具记录的是 raw HTTP 流没有语义解析能力——它知道你发了个 POST但不知道这是gpt-4o-mini的 chat 接口还是dall-e-3的图像生成接口更没法自动提取prompt_tokens和completion_tokens。Hindsight 的设计哲学很明确观测点必须紧贴业务逻辑层而非网络层数据结构必须带语义而非 raw bytes部署方式必须与开发环境同构而非额外加一层基础设施。所以它选择在 SDK 调用前一刻做 hook用functools.wraps包裹原函数在真正发起 HTTP 请求前把 Python 对象序列化为带类型标记的 JSON保留datetime、Enum、bytes等特殊类型同时记录调用栈深度、线程 ID、协程 ID对 asyncio 支持、以及当前os.environ中所有OPENAI_*相关变量快照。这不是简单的 log而是构建了一个“请求快照request snapshot”的概念每个 snapshot 是一个自包含的、可序列化的数据单元包含输入、预期输出、执行环境、时间戳、唯一 trace_id甚至还能关联到 Git commit hash 和当前 Python 虚拟环境的pip list --freeze输出。这种设计让 Hindsight 天然适配 Docker 环境——你不需要在容器里装 mitmproxy只要把hindsight包和一行初始化代码打进镜像它就能在容器内部完成全部观测。这也是为什么热搜词里反复出现docker,docker desktop,openai api key——因为真实生产环境里问题永远发生在“那个跑在 Ubuntu 容器里的 Python 服务用着旧版 openai1.32.0key 存在环境变量里但启动命令漏写了-e OPENAI_API_KEYxxx”。Hindsight 把这个场景下的诊断链条从“猜环境 → 查日志 → 抓包 → 对比文档”压缩成“打开 Hindsight Web UI → 点击失败请求 → 看 snapshot 详情页 → 发现OPENAI_API_KEY是空字符串”。3. 核心模块解析从请求拦截到可视化回溯的四层实现Hindsight 的核心不是单个功能而是四层递进式的数据处理流水线拦截层Intercept、序列化层Serialize、存储层Store、呈现层Visualize。每一层都针对 LLM 调用的特殊性做了定制化设计而不是简单套用通用框架。3.1 拦截层SDK 无关的通用 Hook 机制Hindsight 不绑定openai官方 SDK也不强依赖httpx或requests。它的拦截机制基于 Python 的sys.settrace和threading.setprofile双钩子但做了关键优化只在明确启用时激活且仅追踪目标函数调用。具体实现分三步首先通过inspect.signature动态分析目标函数如openai.chat.completions.create的参数签名识别出哪些参数是dict,list,str,int等基础类型哪些是pydantic.BaseModel实例OpenAI v1.x SDK 的核心数据结构其次在函数入口处插入一个hindsight.trace装饰器该装饰器不修改原函数逻辑而是在__call__前创建一个RequestContext对象将所有参数 shallow copy 后存入其input字段并记录time.perf_counter()作为起始时间戳最后在函数返回或抛出异常时触发on_exit回调填充output或error字段并计算耗时。这个设计的关键优势在于完全兼容异步对于async def create(...)装饰器会自动识别await行为用asyncio.current_task()获取协程上下文确保trace_id在整个 async chain 中保持一致。实测中它能无缝支持openaiv0.28老版、v1.x新版、anthropic、google-generativeai甚至自定义的requests.post(https://api.xxx.com/v1/chat)封装函数——只要你告诉它“这个函数调用代表一次 LLM 请求”它就能工作。 提示不要试图用monkey patch替换requests.Session.send那会导致 SDK 内部重试逻辑失效且无法获取高层语义比如 model name、response format。Hindsight 的拦截点选在“业务意图明确处”而非“网络发送处”这是它稳定性的根基。3.2 序列化层带语义还原的 JSON 编码器LLM 请求数据里充满“不可 JSON 序列化”的东西datetime对象、Enum成员、bytes比如 base64 图片、甚至numpy.ndarray某些多模态模型输入。普通json.dumps会直接报错。Hindsight 的序列化器HindsightJSONEncoder采用分层策略第一层对pydantic.BaseModel实例调用其.model_dump()方法v2.x或.dict()方法v1.x确保字段名、默认值、验证后数据完整保留第二层对datetime转为 ISO 8601 字符串并附加时区信息2024-06-15T14:23:18.12308:00避免时区混淆导致的 timestamp 错误第三层对bytes先尝试 UTF-8 解码为 str失败则用 base64 编码并标记encoding: base64第四层对Enum取其.name和.value双字段防止只存 name 导致后续反序列化丢失数值含义。更重要的是它会主动注入元数据在序列化后的 JSON 根对象里添加_hindsight_meta字段包含sdk_version如openai1.42.0、python_version3.11.9、platformlinux-x86_64、trace_idUUID4、parent_trace_id用于链路追踪。这个 meta 字段让每个 snapshot 都自带“环境指纹”当你在 Docker 容器里看到一个401错误时一眼就能确认这个请求来自openai1.38.0而你本地开发环境是1.42.0版本差异可能导致 auth header 构造方式不同——这就是unexpected status 401 unauthorized的真实原因而非 key 本身错误。3.3 存储层内存优先 可插拔后端的双模设计Hindsight 默认使用concurrent.futures.ThreadPoolExecutorqueue.Queue实现内存队列存储所有 snapshot 先入队再由后台线程批量写入。这保证了主业务线程零阻塞即使 Web UI 暂时不可用数据也不会丢失。队列大小默认设为 1000超过则触发 LRU 清理——不是丢弃而是将最老的 snapshot 归档到磁盘的hindsight_archive/目录用zstd压缩文件名含日期和 hash便于离线分析。但真正的灵活性在于它的后端插件系统。Hindsight 定义了StorageBackend抽象基类内置三种实现MemoryBackend默认、FileBackend写入 JSONL 文件每行一个 snapshot、SQLiteBackend建表snapshots (id, trace_id, created_at, input_json, output_json, error_json, meta_json)。你可以一行代码切换hindsight.configure(storage_backendSQLiteBackend(hindsight.db))。为什么 SQLite 是生产推荐因为它支持 SQL 查询SELECT * FROM snapshots WHERE error_json LIKE %401% AND created_at 2024-06-15能快速定位某天所有认证失败SELECT COUNT(*), json_extract(meta_json, $.sdk_version) FROM snapshots GROUP BY json_extract(meta_json, $.sdk_version)能统计各 SDK 版本的调用占比发现老旧版本是否集中报错。而热搜词里频繁出现的docker install mysql8.0、docker compose其实暗示了另一种需求当团队规模扩大需要共享观测数据时Hindsight 提供PostgreSQLBackend插件需pip install hindsight[postgres]它把 snapshot 当作 JSONB 字段存入 PG利用操作符做高效全文检索比如WHERE input_json {model: gpt-4-turbo}。这种设计让 Hindsight 既能单机调试也能集群部署完全匹配从个人项目到企业级 SaaS 的演进路径。3.4 展示层聚焦“可行动洞察”的 Web UIHindsight 的 Web UI 不是 Grafana 那种通用仪表盘而是专为 LLM 问题诊断设计的“手术台”。首页是时间线视图Timeline View按created_at倒序排列所有 snapshot每条记录显示status绿色 success / 红色 error / 黄色 warning、modelgpt-4o、latency324ms、tokensprompt: 128, completion: 42、trace_id可点击。点击任一记录进入详情页Detail View分三栏布局左栏是Input高亮显示messages数组对长文本自动折叠点击“展开全部”才加载完整内容避免页面卡顿中栏是Output或Error对400错误会解析error.message并用红色边框标出关键句如This models maximum context length is 1048576 tokens并在下方给出即时建议“检测到您传入的 prompt 长度为 1,048,582 tokens超出限制 6 tokens。建议1) 使用tiktoken计算实际 token 数2) 启用truncation_strategyauto参数。”——这个建议不是静态文案而是基于 snapshot 中input.messages实际内容调用tiktoken.encoding_for_model(gpt-4o)动态计算后生成的。右栏是Meta Context展示sdk_version、python_version、environment variables只显示OPENAI_*,ANTHROPIC_*等敏感前缀变量且 key 值用***掩码、call stack精简到 3 层显示app.py:42 in generate_response。最底部是Related Snapshots基于trace_id和parent_trace_id自动关联同一链路的其他请求比如一次“用户提问 → 检索知识库 → 生成回答”的三步调用会全部列在这里形成完整因果链。这个 UI 的设计原则是所有信息都导向一个动作——要么复制 curl 命令复现问题要么下载 snapshot JSON 交给后端排查要么点击“标记为已解决”归档。它不鼓励你“看数据”而是推动你“解决问题”。4. 实操全流程从零部署 Hindsight 到定位一个真实的401错误现在我们来走一遍完整实操假设你正在开发一个基于 Flask 的聊天应用用户反馈“有时发消息就报错说 API key 不对”而你本地测试一切正常。目标是用 Hindsight 在 Docker 环境中复现并定位问题。4.1 环境准备Docker Desktop Python 3.11 基础镜像首先确认你的开发机已安装 Docker DesktopWindows/macOS或 Docker EngineLinux。打开终端运行docker --version确保输出类似Docker version 24.0.7。接着创建项目目录mkdir hindsight-demo cd hindsight-demo新建requirements.txt内容为flask2.3.3 openai1.42.0 hindsight0.8.1注意hindsight是 pip 可安装的独立包pip install hindsight不是 GitHub repo避免新手误入 clone 源码的坑。新建app.py写一个极简 Flask 服务from flask import Flask, request, jsonify import openai app Flask(__name__) # 初始化 Hindsight关键必须在 openai 配置前 import hindsight hindsight.configure( storage_backendhindsight.SQLiteBackend(hindsight.db), web_ui_enabledTrue, web_ui_port8000 ) # 配置 OpenAI从环境变量读取模拟真实部署 openai.api_key os.getenv(OPENAI_API_KEY, sk-xxx) app.route(/chat, methods[POST]) def chat(): data request.json try: response openai.chat.completions.create( modelgpt-3.5-turbo, messages[{role: user, content: data.get(query, hello)}] ) return jsonify({reply: response.choices[0].message.content}) except Exception as e: return jsonify({error: str(e)}), 500 if __name__ __main__: app.run(host0.0.0.0:5000)这里的关键点是hindsight.configure()必须在openai.api_key ...之前执行否则 Hindsight 无法捕获 SDK 初始化过程中的环境变量快照。web_ui_enabledTrue会自动启动内置的 FastAPI Web 服务监听8000端口。4.2 Dockerfile 构建解决docker安装教程中的典型陷阱新建Dockerfile内容如下FROM python:3.11-slim # 设置工作目录 WORKDIR /app # 复制依赖文件先复制 requirements.txt利用 Docker layer cache COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # 复制应用代码 COPY app.py . # 创建非 root 用户安全最佳实践避免热搜词里“docker安装mysql8.0并使用”常忽略的权限问题 RUN addgroup -g 1001 -f appgroup adduser -S appuser -u 1001 # 切换到非 root 用户 USER appuser # 暴露端口Flask 5000 Hindsight UI 8000 EXPOSE 5000 8000 # 启动命令关键必须用 exec 形式否则信号无法传递给进程 CMD [python, app.py]构建镜像docker build -t hindsight-demo .注意不要用docker run -it hindsight-demo直接运行因为缺少OPENAI_API_KEY环境变量。这是unexpected status 401 unauthorized的经典来源——镜像构建成功但运行时 key 未注入。4.3 启动与问题复现用docker run注入 key 并触发错误运行容器注入 keydocker run -p 5000:5000 -p 8000:8000 \ -e OPENAI_API_KEYsk-proper-key-here \ --name hindsight-app \ hindsight-demo此时服务在http://localhost:5000可访问Hindsight UI 在http://localhost:8000。用 curl 测试curl -X POST http://localhost:5000/chat \ -H Content-Type: application/json \ -d {query: Explain quantum computing}正常应返回回答。现在故意制造401错误停止容器重新运行但传入错误 keydocker stop hindsight-app docker run -p 5000:5000 -p 8000:8000 \ -e OPENAI_API_KEYsk-invalid-key \ --name hindsight-app \ hindsight-demo再次 curl得到{error: Error code: 401 - {error: {message: Incorrect API key provided..., type: invalid_request_error, param: None, code: invalid_api_key}}}。立刻打开http://localhost:8000在 Timeline View 中你会看到一条红色记录status显示errorlatency很短100ms说明请求根本没到 OpenAI 服务器而是 SDK 本地校验失败。4.4 深度诊断从 snapshot 中提取决定性证据点击这条红色记录进入 Detail View。重点看右栏Meta Context下的Environment VariablesOPENAI_API_KEY: ***invalid-key***注意这里显示的是***invalid-key***不是完整的sk-invalid-key。这是因为 Hindsight 对敏感变量做了掩码但掩码规则是保留前 3 位和后 4 位中间用***替代。所以sk-invalid-key变成sk-***key。再看Input栏messages数组正常model是gpt-3.5-turbo没问题。关键在Error栏Hindsight 解析了错误 JSON显示Error Type: invalid_api_key Error Message: Incorrect API key provided. Make sure you are sending a valid secret key.但这时你可能会疑惑我传的 key 明明是sk-invalid-key为什么 Hindsight 显示sk-***key这就引出了一个隐藏陷阱OpenAI SDK 会自动 strip key 字符串两端的空白符。如果你的环境变量里不小心有换行符比如OPENAI_API_KEYsk-invalid-key\nSDK 会 trim 成sk-invalid-key但 Hindsight 记录的是 trim 前的原始值。为了验证我们看Meta里的Call Stackapp.py:22 in chat - openai.chat.completions.create(...)说明错误发生在 SDK 内部。现在打开http://localhost:8000的 Console 标签页Hindsight UI 内置的浏览器控制台输入// 查看最近 5 个 snapshot 的原始 input fetch(/api/snapshots?limit5).then(r r.json()).then(data console.table(data.map(s ({id: s.id, key_masked: s.meta.environment.OPENAI_API_KEY, sdk_version: s.meta.sdk_version}))))结果会显示id key_masked sdk_version abc123 sk-***key openai1.42.0 def456 sk-***key openai1.42.0 ...所有记录都是sk-***key证明 key 在注入时就被截断了。这时你应该检查 Docker 运行命令-e OPENAI_API_KEYsk-invalid-key这个字符串如果是在 Windows PowerShell 里执行可能被转义或者 key 文件里有 BOM 字节。Hindsight 的价值在此刻体现它不告诉你“key 错了”而是告诉你“你传入的 key 是sk-invalid-keySDK 版本是1.42.0错误发生在openai\lib\api_resources\chat_completion.py第 87 行”让你精准定位到问题源头是环境变量注入环节而非代码逻辑。4.5 生产加固用 docker-compose.yml 实现一键启停与数据持久化单个docker run命令适合调试但生产需docker-compose。新建docker-compose.ymlversion: 3.8 services: app: build: . ports: - 5000:5000 - 8000:8000 environment: - OPENAI_API_KEY${OPENAI_API_KEY} volumes: - ./hindsight_data:/app/hindsight_data # 持久化 SQLite DB 和 archive restart: unless-stopped # 可选加一个 nginx 反向代理把 /hindsight/ 路径映射到 8000 端口 nginx: image: nginx:alpine ports: - 8080:80 volumes: - ./nginx.conf:/etc/nginx/conf.d/default.conf depends_on: - app对应nginx.confserver { listen 80; location /hindsight/ { proxy_pass http://app:8000/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }这样http://localhost:8080/hindsight/就是 Hindsight UI而http://localhost:5000是你的应用 API。volumes确保hindsight.db不随容器删除而丢失符合docker安装mysql8.0并使用中强调的数据持久化原则。启动只需OPENAI_API_KEYsk-your-real-key docker-compose up -d停止用docker-compose down。整个流程从写代码到定位401不超过 15 分钟且所有步骤均可复现、可审计、可分享。5. 常见问题与避坑指南那些热搜词背后的真实痛点Hindsight 的设计直指 LLM 开发中最让人抓狂的几类问题。以下是根据真实用户反馈整理的高频问题清单每个都附带 Hindsight 下的定位方法和底层原理。5.1unexpected status 401 unauthorized: incorrect api key provided的七种变体这个错误看似简单实则原因多样。Hindsight 能区分以下场景错误现象Hindsight 可见证据根本原因解决方案OPENAI_API_KEY显示***全星号Environment Variables中 key 值为***环境变量未设置os.getenv返回NoneSDK 尝试用None作为 key检查docker run -e或docker-compose.yml的environment字段确认变量名拼写OPENAI_API_KEY不是openai_api_keykey_masked显示sk-***但长度异常如sk-***xCall Stack显示错误在openai\lib\api_resources\chat_completion.py第 87 行key 字符串含不可见字符如\u200b零宽空格SDK trim 后仍非法用 echo $OPENAI_API_KEYsdk_version是openai0.28.0Meta栏明确显示 SDK 版本老版 SDK 使用openai.Completion.create()auth header 格式为Authorization: Bearer key而新版是Authorization: Bearer key但要求 key 以sk-开头升级 SDKpip install --upgrade openai并更新代码语法Input栏messages为空数组InputJSON 中messages: []业务代码逻辑错误传入空列表SDK 仍会发请求OpenAI 返回 401检查上游数据源加if not messages: raise ValueError(Empty messages)防御Error栏显示AuthenticationError: No such organizationerror.code为no_such_organizationkey 属于另一个组织或该组织已被禁用见热搜词api error: 400 this organization has been disabled登录 OpenAI Platform确认 key 对应的 Organization 是否 active或创建新 key实操心得我踩过的最大坑是 Windows 用户用 Notepad 保存.env文件时默认编码是ANSI导致OPENAI_API_KEYsk-xxx中的变成乱码。Hindsight 的environment variables快照会显示OPENAI_API_KEY: k-xxx一眼就能发现编码问题。解决方案用 VS Code 保存为 UTF-8 without BOM。5.2api error: 400 this models maximum context length is ...的 token 计算误区1048576 tokens这个数字常让人误以为是字符数。Hindsight 的Input栏会显示prompt_tokens_estimated: 1048582比限制多 6 个但你肉眼数messages里的文本可能只有几百字。这是因为Token 不等于字符英文中university是 3 个 tokenuni,vers,ity中文里人工智能是 4 个 token人,工,智,能取决于模型 tokenizer。System message 也占 token即使你没传systemroleSDK 可能自动注入默认 system prompt。Function calling 的 schema 占大量 token如果你用了tools参数整个 tools JSON Schema 会被 tokenizer 处理。Hindsight 的解决方案是在Input栏下方提供一个Calculate Tokens按钮。点击后它会调用tiktoken.encoding_for_model(gpt-4o)对messages数组逐项 encode然后显示详细 breakdownSystem message (if any): 12 tokens User message: 1,048,560 tokens Tool schema (if present): 10 tokens Total: 1,048,582 tokens并高亮超限部分。你还可以在 UI 里编辑messages内容实时看到 token 数变化无需反复调用 API 测试。5.3 Docker 环境下connection refused的三层排查法当curl http://localhost:5000/chat返回Failed to connect to localhost port 5000: Connection refusedHindsight 帮你分三层排查第一层容器是否真的在运行docker ps看容器状态。如果STATUS是Exited (1)说明启动失败。Hindsight 的hindsight_data/hindsight.log里会有 traceback比如ModuleNotFoundError: No module named flask证明requirements.txt没装全。第二层端口是否正确暴露docker inspect hindsight-app | grep -A 10 Ports。如果输出为空说明EXPOSE没生效或docker run -p没指定。Hindsight 的Meta栏会显示platform: linux-x86_64但如果你在 M1 Mac 上运行linux/amd64镜像也会connection refused此时platform会是darwin-arm64提示架构不匹配。第三层应用是否监听了正确地址Flask 默认监听127.0.0.1:5000但在容器里必须用0.0.0.0:5000。Hindsight 的Call Stack会显示app.run(host0.0.0.0:5000)如果这里写的是host127.0.0.1UI 会标红警告“检测到 host127.0.0.1容器内无法从外部访问”。5.4llm as judge场景下的 snapshot 关联技巧当用 LLM 做自动评估如judge模型对response打分一次任务会产生多个请求query → candidate_response → judge_prompt → judge_response。Hindsight 通过trace_id和parent_trace_id自动关联。例如你在代码中这样写# 主请求 with hindsight.trace(generate_response): response openai.chat.completions.create(...) # 评估请求显式关联 with hindsight.trace(judge_response, parent_trace_idresponse.trace_id): judge openai.chat.completions.create(...)Hindsight UI 的Related Snapshots栏就会显示这两条记录并用箭头连接。你可以点击judge_response然后看Input里的messages是否包含了candidate_response的全文——这能验证 prompt injection 是否成功避免llm as judge场景中常见的“judge 没看到完整 response”的问题。6. 进阶扩展从 Hindsight 到 LLM 操作系统的雏形Hindsight 的定位是“观测层”但它预留了通往更复杂系统的接口。当你开始处理spatial llm空间大模型、mineru api多模态推理 API或deepseek api时它的扩展性就显现出来。6.1 多模态支持捕获图像与音频的二进制流openai的gpt-4-vision或dall-e-3接口会传image_url或image字段base64。Hindsight 的序列化层会自动识别bytes类型将其转为 base64 并标记encoding: base64。在 UI 的Input栏如果检测到image字段会渲染一个img srcdata:image/png;base64,...预览图。对于音频whisperAPI 的file参数是BytesIO对象Hindsight 同样能捕获并提供audio/mpegMIME type 预览。这解决了open ai 官方的 image gen skill调试中最头疼的问题你传了图但不知道 SDK 是否正确编码或者 OpenAI 服务器是否接收到了正确的尺寸。6.2 LLM 网关集成作为llm 网关的审计模块很多团队用llama.cpp、vLLM或Text Generation Inference搭建私有 LLM 网关。Hindsight 可以部署在网关前端拦截所有/v1/chat/completions请求。只需修改网关的 reverse proxy 配置把流量先打到 Hindsight 的 http://hindsight:8000/proxy