大模型应用工程
多模型统一接入、调用链治理与 Token 计量。把“能对话”做到“可运营、可核算成本”。
01 / 技术方向
不是罗列名词,而是每个方向背后都有对应的代码、踩过的坑和做出取舍的地方。
多模型统一接入、调用链治理与 Token 计量。把“能对话”做到“可运营、可核算成本”。
分块策略、混合检索与证据门槛。让回答可溯源、可引用,也敢对没收录的问题说不。
工具调用的边界设计、只读能力约束与 Streamable HTTP 传输,把系统变成模型真正用得上的接口。
上下文组织、代码编辑与隔离沙箱执行。验证“AI 到底能不能改对代码”这件事。
Go/TypeScript/Python 三栈协同,数据库迁移、可观测性与发布纪律的落地细节。
鉴权模型、审计留痕、内容审核与备案要求。技术方案最终要经得起合规检查。
02 / 项目实践
从服务端到端上,从知识库到移动端。每个项目都保留着真实的技术选型理由和未完成的待办。
桌面端改代码 + 浏览器端问答,平台侧统一供给模型并计 Token
一个面向个人与小团队的 AI 开发平台:桌面端负责读取本地仓库、改代码, 浏览器端负责基于对话的问答与检索,两边共用同一套会话、配额与鉴权。 平台不把供应商密钥散落在业务里,而是收口成一层模型网关。
auth-api、admin-api、chat-api、model-gateway、sandbox-orchgo.work 组织多 module workspace,公共库只放能力、不放业务模型pgx/v5,查询由 sqlc 生成,迁移统一走 goose一套可复刻的“Markdown 知识库 + 只读 MCP 接口”
知识库不是数据库,而是一个受契约约束的文件夹:kb/<id>/wiki/*.md 是唯一事实来源,
服务端零第三方依赖,通过 MCP Streamable HTTP 把“客户原话 → 命中文档 → 原文”交给平台,由平台自己的模型作答。
readOnlyHint=true,服务端不持有写权限node:sqlite面向乡村的 AI 导览与内容社区,试点上海市崇明区三星镇新安村
一个 H5 优先的乡村应用:村民与游客可以问 AI 问题、看卡片、发攻略、打卡, 运营侧有完整的内容发布与审核后台。后端是单台服务器上的轻量栈,前端一套代码同时产出 H5 与 Android 安装包。
/api、/ai、/uploads、/admin,数据库与 API 不对外暴露银行理财排行 + 持仓收益的精算台账
个人向的手机端理财工具:看银行理财排行、对比产品、录入持仓、算清收益。 H5 一套前端,可被微信小程序 web-view 与 APP WebView 直接套壳,后端开箱即用(SQLite),也能切 PostgreSQL。
.env 区分接口基址以 AIDEV 为例:接入层与业务服务之间不传递供应商密钥,所有模型调用都要经过网关。
浏览器端与桌面端共用会话、配额与鉴权,令牌为 ES256 签名的 JWT,有效期与作用域都可收敛。
组装上下文、编排工具调用、以流式方式回传;需要事实依据时转向知识库取原文。
多供应商路由、租户级限流、Token 预结算。请求前预估只用于准入,请求后按用量结算。
代码与工具在隔离环境中执行,结果回传主链路。跑不通就明确失败,不做兜底式编造。
03 / 技术笔记
每个主题挑一段最值得复盘的取舍。点击任意卡片展开阅读。
取最高分的检索永远有返回值,于是模型永远有东西可以顺着编。我们改成绝对门槛裁决命中……
只读不是洁癖,是边界。工具越少,模型选错的概率越低;返回里带证据,上游才能做引用……
业务服务最不该做的事,就是各自持有供应商 Key。网关要薄,薄到可以是唯一需要严格 SLA 的组件……
根目录不是 module,直接 go build ./... 会报错——这不是 bug,是 workspace 的语义……
中文没有天然分词边界,通用分词器在业务黑话上经常切错。切成 bigram,反而稳了……
一套代码要同时出 H5、小程序壳和 Android 安装包,问题很快就变成“哪些能力不能碰”……
04 / 关于本站
本站由个人独立维护,用于记录人工智能相关技术的学习与实践:大模型应用工程、检索增强生成、 Agent 与 MCP 协议、AI 编程助手,以及上述四个项目的技术复盘与架构取舍。
内容以原创整理为主,尽量写清楚“为什么这样选”而不仅是“用了什么”。 涉及第三方资料时会标注来源;文中观点仅代表个人实践结论,不构成任何形式的技术承诺或投资建议。
本站为非经营性个人站点:不提供付费服务、不承接商业推广、不设置用户注册与评论功能, 也不收集与展示任何个人敏感信息。
检索 · 2026-10-28
多数检索系统的默认策略是“取最高分”:把 top-1 片段交给模型,让模型自己判断有没有用。这在 FAQ 场景里会持续制造幻觉——用户问了一个知识库里根本没有的问题,检索依旧返回一个“最高分”的片段,模型便顺着它编出一个看起来很像答案的东西。
我们的做法是引入绝对证据门槛。别名强命中、BM25、菜单路径、图谱一跳这几路只负责“生成候选”,最终是否算命中,由 hasEvidence() 用绝对阈值裁决。分数达不到门槛就明确返回 none,让上游回答“知识库暂未收录”,而不是硬答。
相对归一化(把最高分拉到 1.0 再看分布)在只有一条结果时会给出满分,在语料变化后阈值含义也会漂移。绝对门槛的语义稳定得多:它回答的是“这段文本到底像不像在讲这个问题”,而不是“它是不是本轮里最好的那个”。
收益是错误答案的绝对数量显著下降。在内部问答场景里,我们宁可多一次“不知道”,也不要一次编造——因为一次编造就会让人不再信任整个知识库。
MCP · 2026-10-15
把知识库接给 AI 平台有很多种方式:给一个 API Key 让它直连数据库、把它包成一个 RAG 服务、或者用 MCP 暴露成一组工具。我们选了第三种,并把工具数量压到 6 个、全部标注 annotations.readOnlyHint = true。
知识库的唯一事实来源是 kb/<id>/wiki/*.md 这些 Markdown 文件,写入只发生在人工整理阶段。服务端因此可以完全不持有写权限,也不会因为模型的一次误调用而污染语料。这条约束省掉了后面所有的“回滚与审计”问题。
工具越多,模型选错的概率越高,上下文里用于描述工具的 token 也越多。6 个工具覆盖检索、取文档、列目录、取元信息、健康检查、词汇表查询,剩下的能力通过参数表达,而不是新增工具。
每个工具的返回都带 docId、分数与命中证据(命中别名、证据类型、片段),上游才能做引用与溯源——不带证据的检索结果,等于要求模型无条件相信你。
一次请求带够上下文,服务端不保留会话状态。好处是扩容和重启都不需要额外协调,坏处是每次都要传完整参数。对这个体量的知识库,我们选前者。
网关 · 2026-09-30
平台侧同时接了多家模型供应商之后,业务服务最不该做的事就是各自持有供应商 Key。我们把这些收口到一个 model-gateway:所有调用方只认平台令牌,网关负责三件事。
ES256 签名的 JWT 里带租户、角色与配额标识。网关只做校验不做签发——签发在 auth-api,密钥只在那里存在。这样即使网关被攻破,也拿不到签发能力。
按“租户 + 模型”两级桶。只按租户限流会让一个租户的批量任务把某个模型打到不可用;只按模型限流又会让单个租户无限制地挤占公共容量。两级桶的代价是要维护更多计数键。
请求前按 prompt 长度预估,请求后按供应商返回的 usage 结算,差额写入流水。预估值只用于准入判断,结算值才是账单依据。这条区分很重要:把预估当账单,对账时一定对不上。
网关不做 prompt 拼装、不做结果缓存、不感知业务字段。薄的好处是它可以是整条链路上唯一需要严格 SLA 的组件,也唯一需要独立扩容。
Go · 2026-09-18
服务端是 5 个 Go 服务:auth-api、admin-api、chat-api、model-gateway、sandbox-orch。我们用 go.work 把它们和一个公共库 packages/go 放进同一个 workspace。
仓储根目录本身不是 module,直接在根目录执行 go build ./... 会报 directory prefix . does not contain modules listed in go.work。这不是配置错,而是 workspace 的语义:每个 module 独立构建。
所以我们在脚本里按 workspace 逐个模块执行验证,而不是在根目录跑一条 go build。把这条写进 CI,比在 README 里反复解释有用。
建 services/<name>/go.mod(module 路径同前缀、go 1.23)→ go work use ./services/<name> → go work sync。不要在服务里复制公共库代码,直接 import。
检索 · 2026-09-02
中文 FAQ 的典型形态是短问题 + 术语密集的答案,比如“单子不要了怎么弄”。这类查询有三个特点:查询短、专有名词多、同义改写多。
向量检索对语义改写很友好,但对“专有名词必须精确命中”不敏感。而业务系统里最要命的恰恰是专有名词——“作废”和“退单”算不算同一件事,得由业务规则说了算,不能交给相似度去猜。
中文没有天然分词边界,通用分词器在业务黑话上经常切错。把查询切成相邻两字组合(bigram),既避免了分词错误,又能让“开单”“退单”这类黑话获得精确权重。BM25 的 IDF 会自然压低泛词、抬高黑话。
在内部知识库上,bigram BM25 的 top-1 命中率高于同语料的向量检索,且不需要向量库、不需要模型 Key,冷启动成本接近零。
代价是它对“换了说法但意思相同”的查询较弱。所以我们在 BM25 之外补了别名强命中与菜单路径两路,用规则兜住长尾——这也解释了为什么最终的检索是四路融合,而不是选一个最好的。
移动端 · 2026-08-20
稻梦空间最早只要求 H5,验收也在浏览器里做。真正要装到村民手机上时,问题变成:一套代码怎么同时出 H5、微信小程序壳和 Android 安装包。
我们按小程序的能力集写页面:没有 DOM、没有 window、只有组件与路由。编译到 H5 时由框架补齐差异。约束看起来更严,但换来的是一份业务代码能同时进小程序 web-view 和 Capacitor 壳。
试点阶段改一版就要推到手机上,走应用商店审核来不及。所以 APK 用 release 签名打包,App 启动时读一份版本清单,发现新版本就下载 APK 并调起系统安装。
代价是要自己维护版本清单与分发路径,也要处理下载失败、安装被拦等分支。对一个试点项目,这是可以接受的。
.env 基址必须分开,混用会出现“白屏但接口 200”;