chore: T-P-1 工作区收敛——并行会话成果与历史未入库文件整理入库

- 入库历史遗漏源码/测试:router_system 9 模块(agent/executors/inference/knowledge/
  memory/planner/skills/trace)、tests 11 个测试文件、config/knowledge 领域知识
- 入库根目录方案文档(v2/v3/可行性×2)、references 文献(arxiv 14-18/cnki_open/
  参考文献清单)、research 论文素材(routerarena/paper/中文文献 PDF)
- 前端构建产物刷新(新 hash);webapp 误写文档删除
- gitignore 增补:deepseek-harness、research/_refs、.mimosa/.zcode、网关日志/pid、
  临时调试脚本、tests/e2e/node_modules、AI代理功能开发/prefix
- 基线确认:318 passed
This commit is contained in:
tzt
2026-09-05 08:28:25 +08:00
parent 747d85c3ba
commit ce0f6170d3
82 changed files with 74132 additions and 36 deletions
+33
View File
@@ -0,0 +1,33 @@
# AI 代理功能开发 · 校园 AI 代理层
> **定位标注**:本目录开发「端 ↔ 云之间的 **AI 代理层**」——面向校园场景的 LLM API 代理网关
> (如代理 DeepSeek key:学生持代理 key,代理持有上游主 key)。
> **商业模式 = API 差价 + 缓存收益**:校园问题高重复 → 高缓存命中 → 高毛利;
> 校园网提供网络基础设施(托管/带宽/内网可达)。
> **方案全文见 [`方案_校园AI代理层.md`](方案_校园AI代理层.md)**(架构 / 缓存经济学 / 复用映射 / MVP 任务 / 合规红线)。
## 当前状态
- [x] 立项草案(2026-09-04):方案文档就绪
- [ ] P1 透传网关 → P2 计量计费 → P3 缓存栈 → P4 账号配额 → P5 看板(见方案第 4 节)
## 工作区约定(智能体工具调用)
本目录同时是智能体的工作区之一:
1. **接入**:设置页 → 工作区选择本目录;或 `POST /agent/workspaces`
`{"path": "AI代理功能开发"}`。网关默认工作区仍为 `agent_workspace/`,互不影响。
2. **安全边界**(对齐 实现方案_v4 / D12):工具调用关押在本目录内,路径越界拒绝;
`run_command` 默认关闭(allow_shell 开关)。
## Git 与安全约定
- 本目录**仅 README 与方案文档入库**;开发/实验产物由 `.gitignore` 忽略。
- **上游主 key 只存 env / `config/settings.json`(已 gitignore**
任何代码、示例、测试不得出现真实凭据字面量。
## 相关文档与代码
- 复用资产:`gateway/model_pool.py`(多价位池)、`router_system/cache.py`(语义缓存)、
`gateway/api.py`SSE 基建)、`review.py`sqlite 模式 → billing 参照)
- 设计:`实现方案_v4_模型池与工具智能体.md`;进展:`毕业设计_进度记录.md`
@@ -0,0 +1,302 @@
# 实施方案:校园 AI 代理层与缓存层(T-P0…T-P8 逐任务执行版)
> 编写日期:2026-09-05 状态:**执行版**(设计依据见《方案_校园AI代理层.md》;两者冲突时以本文为准)
> 预期读者:负责实现的 AI Agent。实现前必读:第 1 节锁定决策、第 9 节测试纪律。
> 总工期估算:约 13 个工作日;里程碑 M1(计费代理)→ M2(缓存栈)→ M3(管理面+验收)。
---
## 0. 范围与非目标
**范围**:现有 FastAPI app 内新增代理面(`/proxy/v1/*`)+学生 key 鉴权计费+两级缓存(精确→n-gram 语义)
+管理端点与前端页+压测报告。
**MVP 非目标**(做了算超纲,除非另有指示):多上游 key 轮换/前缀亲和、embedding 向量检索
(预留 embedder 接口,M3 可选)、空闲时段自动调度(仅实现 `is_offpeak(ts)` 判定)、流式中途
failover、内容审核、多币种。
**不改动**`router_system/`(只允许 import `router_system.cache`);现有 `/chat``/agent`
`/review` 行为与全部既有测试。
---
## 1. 锁定决策(实现中不得擅改)
- **D-P1 货币**:账本一律**毫元整数**(1 元 = 1000 毫元),禁止浮点;展示层格式化。
- **D-P2 桶来源**:请求头 `X-Campus-Bucket`(缺省 `default`);`model→bucket` 可在配置映射。
- **D-P3 上游条目**:复用 `config/model_pool.json`,条目新增可选字段
`"provider": "deepseek|openai|anthropic"`usage 归一化用)与 `"in_hit_price"`(可选,缺省 = in_miss×1/30)。
- **D-P4 failover**:仅在上游**首 token 返回前**允许切换;流中失败 = `aborted`,按已收 usage 计费
(无 usage 按字符估算),**不写缓存**。
- **D-P5 缓存准入**:只缓存 `finish_reason=="stop"` 的**完整单轮**响应;错误/超时/断连/多轮一律不缓存。
- **D-P6 依赖纪律**M1/M2 **零新第三方依赖**(语义缓存用 stdlib 倒排 n-gram);embedding + numpy
属 M3 可选项,启用须登记 requirements.txt 并说明理由。
- **D-P7 开关**`proxy.enabled == false` 时不注册任何 `/proxy` 路由,行为与现状逐字节一致。
- **D-P8 凭据**:上游主 key 只从 env / `config/settings.json`(已 gitignore)读取,代码/测试零字面量。
- **D-P9 进程模型****uvicorn 单进程(workers=1)是正确性前提**——singleflight、令牌桶、内存 LRU
均为进程内状态,多 worker 会静默失效(限流失效/重复打上游)。扩容路径只有性能预算触发的
数据面手术(方案 §3.5),不存在"加 worker"这个选项。
- **D-P10 DB 访问纪律**sqlite3 是同步库,**所有 DB 调用必须经 `asyncio.to_thread`**(写路径可进一步
收敛为单写线程+队列);连接 `check_same_thread=False` + `threading.Lock` 串行化写。
热路径读(key→学生上下文)走进程内缓存:哈希→(student_id,status,caps) 的 LRUTTL 30s,写后失效。
违反此条,T-P8 的 P99 ≤50ms 会被同步 DB 卡死。
- **D-P11 计费两阶段(防并发超扣)**:预扣用**原子 UPDATE**
`UPDATE students SET balance_milli=balance_milli-? WHERE id=? AND balance_milli>=?`rowcount=0 即 402),
est = in_miss 价×字符估算 + out 价×min(max_tokens,4096)(宁可高估);settle 按真实 usage 回补
estactual 差额;void 全额退。不用 holds 表,一条 SQL 解决竞态。
---
## 2. 文件清单
**新建**
```
gateway/proxy/
├── __init__.py # build_proxy_router(settings, pool) -> APIRouter;唯一组装点
├── config.py # ProxyConfig:从 settings 读取 + 校验 + 默认值
├── errors.py # ProxyAuthError(401)/QuotaError(429)/BalanceError(402)/UpstreamError(502)
├── auth.py # key 签发/校验/注销 + 令牌桶 + 日限额
├── ledger.py # DDL 初始化 + students/keys CRUD + hold/settle/void + 日志写入
├── pricing.py # PriceTable + is_offpeak(ts) + compute(usage, model, ts) -> CostBreakdown
├── normalizer.py # bucket 解析 + 规范化哈希 + PrefixShaper(整形后请求体)
├── semcache.py # 精确 L1 + n-gram 倒排 L2 + TTL/版本失效 + singleflight
├── upstream.py # httpx 流式派发 + usage 三家归一化 + 首 token 前 failover
└── routes.py # /proxy/v1/chat/completions、/proxy/v1/models、/proxy/admin/*
scripts/bench_proxy.py # E-P1/E-P2 数据管道:200 条校园模拟请求 → 命中率/毛利报告
webapp/src/views/ProxyView.vue
tests/test_proxy_{auth,ledger,pricing,normalizer,semcache,upstream,routes}.py
```
**修改**`gateway/api.py``app.include_router`,受 `proxy.enabled` 门控,≤5 行);
`webapp/src/App.vue`NAV 加"代理"项);`config/model_pool.json`(条目加 provider/in_hit_price);
`任务拆解与执行计划.md``AGENTS.md`(登记)。
---
## 3. 数据模型(SQLite DDL,库文件 `data/proxy.sqlite3`WAL 模式)
```sql
CREATE TABLE students(
id INTEGER PRIMARY KEY, name TEXT NOT NULL, class TEXT DEFAULT '',
status TEXT NOT NULL DEFAULT 'active', -- active|suspended
balance_milli INTEGER NOT NULL DEFAULT 0,
daily_cap_milli INTEGER NOT NULL DEFAULT 5000,
spent_today_milli INTEGER NOT NULL DEFAULT 0, spent_date TEXT DEFAULT '');
CREATE TABLE proxy_keys(
id INTEGER PRIMARY KEY, key_hash TEXT UNIQUE NOT NULL, key_prefix TEXT NOT NULL,
student_id INTEGER NOT NULL REFERENCES students(id),
created_ts INTEGER NOT NULL, revoked INTEGER NOT NULL DEFAULT 0,
rpm_cap INTEGER NOT NULL DEFAULT 10, day_cap_req INTEGER NOT NULL DEFAULT 200,
req_today INTEGER NOT NULL DEFAULT 0, req_date TEXT DEFAULT '');
CREATE TABLE usage_ledger(
request_id TEXT PRIMARY KEY, ts INTEGER NOT NULL, key_id INTEGER NOT NULL,
model TEXT NOT NULL, bucket TEXT NOT NULL DEFAULT 'default',
in_miss_tok INTEGER NOT NULL DEFAULT 0, in_hit_tok INTEGER NOT NULL DEFAULT 0,
out_tok INTEGER NOT NULL DEFAULT 0, gateway_cached INTEGER NOT NULL DEFAULT 0,
upstream_cost_milli INTEGER NOT NULL DEFAULT 0, charged_milli INTEGER NOT NULL DEFAULT 0,
margin_milli INTEGER NOT NULL DEFAULT 0, ttfb_ms INTEGER, total_ms INTEGER,
status TEXT NOT NULL); -- ok|cached|error|aborted|insufficient
CREATE TABLE semcache(
cache_key TEXT PRIMARY KEY, -- bucket + '|' + sha256(norm_q)
bucket TEXT NOT NULL, q_norm TEXT NOT NULL, answer TEXT NOT NULL, model TEXT NOT NULL,
created_ts INTEGER NOT NULL, ttl_ts INTEGER NOT NULL,
doc_version INTEGER NOT NULL DEFAULT 1, hits INTEGER NOT NULL DEFAULT 0);
CREATE INDEX idx_semcache_bucket ON semcache(bucket, ttl_ts);
CREATE INDEX idx_ledger_ts ON usage_ledger(ts); -- stats 按时间范围聚合用
```
前缀资料文件(`doc_prefix_file` 指向的 txt)不入库不入 git(内容属课程方),gitignore 加
`AI代理功能开发/prefix/`
---
## 4. 配置 schemasettings.json 的 `proxy` 段)
```jsonc
"proxy": {
"enabled": true,
"admin_key": "",
"buckets": {
"default": {"system_template": "你是校园学习助手。", "doc_prefix_file": null,
"doc_version": 1, "ttl_hours": 72},
"course_python": {"system_template": "你是 Python 课程助教。",
"doc_prefix_file": "AI代理功能开发/prefix/python24.txt",
"doc_version": 1, "ttl_hours": 168}
},
"pricing": { // 上游成本价:元/1M tokens;示例为 Flash 档高峰价,以官网价目为准
"deepseek-chat": {"in_miss": 3.0, "in_hit": 0.1, "out": 9.0},
"peak_window": {"start": "08:30", "end": "23:59"}, // 窗口外按 off-peak 系数 0.5
"offpeak_factor": 0.5,
"sale_discount": {"in": 0.5, "out": 0.5} // 学生售价折扣
},
"limits": {"rpm_per_key": 10, "day_req_cap": 200, "concurrent_per_key": 2,
"max_body_chars": 60000},
"semcache": {"enabled": true, "sim_threshold": 0.92, "max_entries": 300000,
"promote_frequency": 5}
}
```
峰值/空闲窗口与官方"高峰时段"定义对齐,上线前按官网核对一次。
---
## 5. API 契约
### 5.1 学生面(OpenAI 兼容)
- `POST /proxy/v1/chat/completions``Authorization: Bearer sk-campus-…`
body = OpenAI chat 格式 + 可选头 `X-Campus-Bucket`。**计量不依赖客户端行为**
代理向上游始终注入 `stream_options: {"include_usage": true}`,客户端未要求 usage 时
过滤该 chunk 不下发。响应与客户端请求同构:stream=true 收 SSEstream=false 收 JSON
**缓存命中也按此回放**SSE 合成,见 §6)。
- `GET /proxy/v1/models` → 池内允许的模型名列表。
### 5.2 管理面(X-Admin-Key 鉴权,见本节末)
| 端点 | 说明 |
|---|---|
| `POST /proxy/admin/students` | 建学生 {name, class, balance_yuan, caps} |
| `POST /proxy/admin/students/{id}/topup` | {amount_yuan} 充值 |
| `POST /proxy/admin/keys` | {student_id, rpm_cap?, day_cap_req?} → **明文 key 只返回一次** |
| `POST /proxy/admin/keys/{id}/revoke` | 注销 |
| `GET /proxy/admin/stats` | {requests, h_g, h_p, revenue, cost, margin, by_bucket} |
| `GET /proxy/admin/ledger?student_id=&limit=&offset=` | 流水分页 |
**管理面鉴权**:请求头 `X-Admin-Key``settings.proxy.admin_key` 比对
`hmac.compare_digest`,防时序侧信道);未配置 admin_key 时仅放行 loopback 来源。
**stats 口径**h_g = cached/requestsh_p = Σin_hit/(Σin_hit+Σin_miss)revenue=Σcharged、
cost=Σupstream_cost、margin=Σmargin;支持 `?since=<ts>` 走 idx_ledger_ts。
归档:usage_ledger 超 180 天的行由 T-P8 顺带清理(年度 650 万行会拖慢聚合)。
### 5.3 错误码
401 无效/注销 key403 学生 suspended402 余额或日上限不足;429 限流;
502 上游失败(首 token 前 failover 均失败);body 超 `max_body_chars` → 413。
---
## 6. 模块接口签名(实现按此,不改名)
```python
# auth.py
def issue_key(ledger, student_id, rpm_cap=None, day_cap_req=None) -> str # 明文仅此一次
def authenticate(authorization: str, ledger, limits, now) -> AuthContext # raises ProxyAuth/Quota
class RateLimiter: allow(key_id, rpm_cap) -> bool
# ledger.py
class Ledger:
def init_db(cls, path) -> Ledger
def upsert_student / topup / set_status(...)
def check_and_count(key_id, student_id, now) -> None # 日限额双检
def try_hold(student_id, est_milli) -> bool # 原子预扣(D-P11),False=402
def settle(request_id, actual_milli, ...) -> None # 结算 + 回补 est-actual 差额
def void(request_id) -> None # 全额退(上游失败)
def record(UsageRow) -> None # 幂等:request_id 主键
# pricing.py
def is_offpeak(ts, window) -> bool
def compute(usage: Usage, model: str, ts: int, cfg) -> CostBreakdown
# CostBreakdown: upstream_cost_milli, charged_milli, margin_milli(全整数毫元)
# normalizer.py
def resolve_bucket(body, headers, cfg) -> BucketCfg
def canonical_hash(bucket, doc_version, body) -> str # 稳定序列化→sha256
def shape(body, bucket_cfg) -> dict # 整形后上游请求体
# semcache.py
class SemanticCache:
def lookup(bucket, doc_version, norm_hash, norm_text, now) -> Hit|None
def put(bucket, doc_version, norm_hash, norm_text, answer, model, now) -> None
def stats() -> {entries, hits, h_g}
# upstream.py —— 模块级 httpx.AsyncClient 单例(keepalivelimits.max_connections=100),
# 超时 connect=10s / read=120s(流式整段)/ write=10s / pool=30s;始终注入 include_usage(§5.1)。
async def stream(body, entry, usage_sink) -> AsyncIterator[bytes]
def normalize_usage(provider, usage_dict) -> Usage # 三家字段→统一 Usage
```
**规范化规则(normalizer,决定缓存命中率的代码,测试最重)**
1. messages 序列化:role 与 content 交替拼接为 `role\u0001content\u0002`
2. 剔除易变字段:`temperature/frequency_penalty/seed/request_id/时间戳类内容行`
3. 系统模板与资料前缀**不参与** L1 哈希(桶+doc_version 已表达),只参与上游整形;
4. 整形后消息顺序固定:`[canonical_system] → [doc_prefix] → [原 messages]`
5. 多轮判定:`len(messages) > 2`system 之外 >1 条)→ `cacheable=False`
**语义相似度精确定义(semcache L2,误命中=返回错答案,此节测试最重)**
- q_norm 取**字符 2-gram + 3-gram 集合**(中文天然适配,无需分词);
- 倒排索引驻内存(gram→cache_key 列表),**启动时由 semcache 表 q_norm 重建**
- 候选门限:共享 gram ≥3 才计分(避免全量比对);分数 = 加权 Jaccard3-gram 权 2、2-gram 权 1);
- ≥ sim_threshold(0.92) 判命中;**L2 语义命中累计 promote_frequency(5) 次后晋升为 L1 精确条目**
(复用 v1 cache.py 的提升模式——考试周变体收敛后自动加速)。
**SSE 合成回放(缓存命中的流式客户端兼容)**:命中时按请求 stream 字段同构返回——
stream=true 则用缓存内容合成 SSE(复用缓存 id/formatcontent 按 ~20 字符分块 +
finish_reason=stop + `data: [DONE]`),stream=false 返回标准 JSON。
**禁止**对 stream=true 客户端直返 JSONOpenAI SDK 会解析失败)。
---
## 7. 请求处理时序(routes.py 主流程)
```
auth(热路径缓存) → 限流/日额 → body 校验(413) → bucket/规范化 → L1 精确查 → L2 n-gram 查
├命中: settle(cached, cost=0, charged=按售价) → SSE/JSON 同构回放(§6
└未命中: singleflight 登记 → try_hold(est 原子预扣,不足 402) → shape(body) → upstream.stream
→ tee: 逐块转发客户端 + 累积(含被过滤的 usage chunk
→ 结束: usage 归一化 → compute → settle(actual,回补差额) → stop 且单轮)semcache.put
→ 异常: void 退预扣 / aborted 按已收 usage 记账;均不缓存;singleflight 广播同一结果
```
singleflight`dict[norm_hash → asyncio.Future]`,**上限 256 条**(超出旁路不合并,防考试周
变体洪峰撑爆内存);第二个到达者 await 同一 Future;等待方 60s 超时自行降级直连。
---
## 8. 测试计划
| 文件 | 必测用例 |
|---|---|
| test_proxy_auth | 签发→鉴权通过;错 key 401;注销 401rpm 429;日请求上限 429 |
| test_proxy_ledger | hold/settle/void 全链条(预扣-结算-回补一致);request_id 幂等;日限额跨日重置(注入日期);**并发 N 路同时预扣,余额不足者精确拒绝、不超扣** |
| test_proxy_pricing | 峰谷边界(假时钟 ±1 分钟);毫元取整不漂移(≥10 组黄金用例);margin = charged cost |
| test_proxy_normalizer | 同语义输入同哈希;易变字段剔除;整形顺序固定;doc_version+1 后旧缓存不可见;多轮 cacheable=False |
| test_proxy_semcache | 精确命中;n-gram 阈值上命中/下未中;TTL 过期(假时钟);失败不缓存;entries 上限 LRU |
| test_proxy_upstream | 三家 usage 形状归一;SSE 透传逐字节一致;首 token 前 failover;流中失败=aborted**usage 注入且客户端未要求时被过滤** |
| test_proxy_routes | 端到端:缓存命中账目(cost=0,charged>0,cached=1);未命中全程;402/429/413singleflight 两并发一次上游(计数 Mock);**stream=true 命中回放为合法 SSE****L2 命中 5 次后 L1 直接命中** |
**隔离清单**:每测试独立 `tmp_path/proxy.sqlite3`;假时钟注入(禁 sleep);MockTransport 假上游;
不动 `config/settings.json` 真实文件(沿用 v4 事故后的备份/恢复模式)。
---
## 9. WBST-P0…T-P8(依赖顺序执行,每任务一 commit `feat(proxy): T-Pn 描述`
| # | 任务 | 产出/步骤 | 验收(可执行命令级) | 估时 |
|---|---|---|---|---|
| T-P0 | 骨架 | gateway/proxy/ 六文件空实现 + DDL + 挂路由(enabled 门控) | `pytest tests -q` 全绿(318→新增骨架测试);开关关闭时 `/proxy/*` 404 | 0.5d |
| T-P1 | 鉴权+账本 | auth.py + ledger.py(原子预扣 try_hold/settle/void + 热路径缓存)+ 管理端点 | test_proxy_auth/ledger 全绿;并发预扣不超扣;curl 用签发 key 过 401 | 2d |
| T-P2 | 上游客户端 | upstream.pyAsyncClient 单例/超时/流式派发 + usage 注入与过滤 + 三家归一化 + failover | test_proxy_upstream 全绿;curl 流式可见逐块输出 | 2d |
| T-P3 | 计价+结算 | pricing.py + 账本结算接线 | test_proxy_pricing 全绿(≥10 黄金用例) | 1d |
| T-P4 | 路由端到端 | routes.py 主时序(不含缓存分支) | OpenAI SDK 指 `/proxy/v1` 对话成功;账本三值一致;402/429/413 正确 | 1.5d |
| T-P5 | 规范化+桶 | normalizer.py + 配置桶 | test_proxy_normalizer 全绿 | 1d |
| T-P6 | 语义缓存 | semcache.py(含倒排索引重建/晋升/LRU+ SSE 合成回放 + singleflight + 接线 + `scripts/warm_prefix.py`(桶前缀预热,max_tokens=1 | test_proxy_semcache 全绿;两并发同请求上游仅 1 次;stream=true 命中回放合法 SSE | 2.5d |
| T-P7 | 管理面+前端 | stats/ledger 端点 + ProxyView.vue(三卡片) | 看板真实数据渲染;`npm run build` 产物更新 | 2d |
| T-P8 | 压测+预算 | scripts/bench_proxy.py200 条(重复≥50%+200 并发;**`--live` 模式**(真实 key 50 条子集测真实 h_p,mock 模式 h_p 为可配置常数仅供联调);顺带 ledger >180d 归档清理 | P99 附加延迟≤50ms;内存≤1GB;账目零不一致;报告入 `AI代理功能开发/bench/` | 1.5d |
依赖链:T-P0 → T-P1 → T-P2 → T-P3 → T-P4 → (T-P5 → T-P6) → T-P7 → T-P8。
**总验收 = M1(T-P0..4) + M2(T-P5..6) + M3(T-P7..8)**E-P1 报告显示 h_g+h_p ≥ 50% 且统一 5 折毛利为正
**h_p 须以 `--live` 实测为准**——mock 上游给不出真实的 prompt_cache_hit_tokens)。
---
## 10. 风险与回滚
| 风险 | 对策 |
|---|---|
| n-gram 语义误命中(相似≠可答) | 阈值 0.92 起步 + 桶隔离 + TTL;E-P3 抽检正确率,误答率高先调阈值再考虑 M3 embedding |
| 单进程瓶颈 | 性能预算压测(T-P8)触发才议数据面手术(见方案 §3.5) |
| 上游调价 | 价格表纯配置热改;model_pool 多供应商条目 |
| 账目漂移 | 毫元整数(D-P1+ request_id 幂等 + T-P8 账目零不一致验收 |
| 测试污染真实配置 | 隔离清单 + settings 备份/恢复模式(v4 事故先例) |
| 误改多 worker 部署 | D-P9 写入部署文档与 serve.py 启动参数校验(workers>1 时拒绝启动并提示) |
| 同步 DB 卡事件循环 | D-P10 强制 to_thread + 代码评审清单项;T-P8 压测显式测 DB 路径 P99 |
@@ -0,0 +1,247 @@
# 方案:校园 AI 代理层(AI Proxy Gateway
> 编写日期:2026-09-04 状态:立项草案(待实现)
> 定位:在「端(学生 / 本地小模型)」与「云(DeepSeek 等 LLM API)」之间加一层 **AI 代理网关**——
> "端云协同"的第三层。开发工作(代码/实验/数据)全部落在本目录 `AI代理功能开发/`。
---
## 0. 一页速览
- **商业模式 = API 差价 + 缓存收益**。代理持有上游主 key(如 DeepSeek),学生持代理 key
学生按明牌单价/包月付费,代理按实际混合成本向上游结算。
- **关键洞察**:校园场景问题高度重复(同课程、同作业、同考点)→ 通过"网关语义缓存直答 +
请求前缀整形"把上游**缓存命中价输入**的占比做到极高(上游缓存命中价通常为未命中价的 1/4~1/10,
以官网最新价目为准),而计费按未命中口径 → 差值即毛利。
- **校园网提供基础设施**:托管、带宽、内网可达零成本;本地小模型层可跑在校内机器 → 近零成本兜底层。
- **北极星指标**:综合缓存命中率 `h = h_g + h_p`(网关语义缓存命中 + 上游前缀缓存命中)与
毛利/千次请求。
## 1. 架构
```
学生(校园网内;Web / SDK / 任意 OpenAI 兼容客户端)
│ ① 代理 key(学生凭据,非上游 key)
┌─ 校园 AI 代理网关(本目录开发)──────────────────────┐
│ ② 鉴权 / 配额 / 限流(学生账户、代理 key 签发注销) │
│ ③ 缓存栈 L0:语义缓存直答(命中 = 上游成本 0,全毛利) │
│ L1:前缀整形(统一 system + 课程资料前置, │
│ 用户问题永远在末尾 → 上游前缀缓存高命中) │
│ ④ 计量计费账本(usage / prompt_cache_hit_tokens 分账) │
│ ⑤ 多价位调度(复用 model_poollocal / budget / premium)│
└──────────────────┬─────────────────────────┘
▼ ⑥ 上游主 keyenv / config/settings.json 注入,绝不入库)
云端 API(DeepSeek 等;前缀缓存自动生效)
```
## 2. 缓存经济学("赚缓存钱"的原理)
上游对**前缀缓存命中**的输入 token 计价通常为未命中价的 1/4~1/10(比例随上游版本变动,
以官网最新价目为准)。代理有三个牟利杠杆,按毛利从高到低:
1. **L0 语义缓存直答**:同义问题直接复用历史答案,上游成本为 0,毛利率 100%;
2. **L1 前缀整形**:所有请求强制统一前缀结构(固定 system 模板 + 课程资料/RAG 内容前置 +
用户问题置末),使上游自动前缀缓存命中率大幅上升,输入成本降至命中价;
3. **计费口径差**:学生按明牌价计费,代理按实际混合成本结算。
毛利公式(输入部分,示意):
```
cost_in = (1 h_g h_p) · P_miss + h_p · P_hit (h_g:网关直答命中率,h_p:上游前缀命中率)
收入 = P_sale · tokens
```
校园场景 `h` 高的三个理由:课件/题库做共享前缀(同一门课几百人同前缀)、考试周问题重复率极高、
班级级 system 模板天然统一。
## 2.5 经济性测算(2026-09-04,讨论"官方价 5 折"定价)
**价格锚点**DeepSeek 2026 分时价,每 1M tokens[官方价目](https://api-docs.deepseek.com/zh-cn/quick_start/pricing)):
输入未命中 空闲 ¥1.5 / 高峰 ¥3.0(Flash 档);输入命中低至 ¥0.025–0.1(≈未命中价 **1/30**);
输出 空闲 ¥4.5 起 / 高峰 ¥9 起。
**命中率规划值**(校园集中域):
h_g 网关语义缓存 保守 15% / 中性 25% / 考试周 4050%
h_p 上游前缀命中(token 加权,前缀整形后)保守 30% / 中性 50% / 乐观 65%。
综合输入命中率:保守 40% / 中性 63% / 考试周 79%。
独有优势:前缀缓存挂在主 key 账号下 → **全校请求共享同一缓存池**(学生各自持 key 做不到)。
**单请求模型**(3K 输入含 1.5K 共享前缀 + 0.8K 输出,高峰):
令 o = 输出成本/输入未命中成本 = 0.8;r = 命中/未命中 = 1/30。
统一 5 折收入 = 0.5(1+o);差异化(输入 5 折、输出 8 折)收入 = 0.5 + 0.8o。
免上游率 h₀ = h_g + 本地分流率(model_pool local 档)。
| 场景 | h₀ | 统一 5 折毛利 | 输入 5 折/输出 8 折 |
|---|---|---|---|
| 保守 | 20% | **34%** | 6%(近打平) |
| 中性 | 40% | +12% | **+31%** |
| 考试周 | 55% | +41% | +54% |
盈亏平衡 h₀:统一 5 折 ≈ 32%;差异化 ≈ **13%**
**结论**:① 全线统一 5 折结构性危险——输出 token 固定亏 50%,缓存利润补不平;
**推荐"输入 5 折 / 输出 8 折"差异化定价**。② 杠杆排序:差异化定价 > 本地分流 > h_g 语义缓存
> 空闲时段调度(成本直接半价)> h_p 前缀整形。③ 规模:300 活跃用户问答场景月毛利仅数百元;
**放大器 = 编程智能体闭环**(单任务 tokens 为问答 30–50 倍,本系统消费本网关,月毛利可上 2000–5000 元)。
### 2.6 规模化测算(2026-09-04:5000 活跃用户 + 学校采纳情景)
**用量假设(制度性流量:课程绑定 + 校赛指定,非自然增长)**
活跃 5000DAU 平时 30%1500/ 高峰周 50%2500);人均日请求 12/18;40 教学周 ≈ **650 万请求/年**
画像:问答 3.5K in + 0.8K out;智能体任务 50K in + 10K out。
**商业运营账**(差异化定价,缓存按规模校准:问答段 h_g 30–40%、智能体段 515%):
| 情景 | 构成 | 年收入 | 年净利 |
|---|---|---|---|
| S1 纯问答 | agent 占 0 | ≈¥5.7 万 | ≈¥1.7 万(30% |
| S2 +10% 智能体 | 校赛试点+编程课 | ≈¥12.8 万 | ≈¥2.9 万(23% |
| S3 +20% 智能体+竞赛按量 | 完整高位优势 | ≈¥20 万+竞赛经费 | ≈¥6 万+竞赛毛利 |
**学校采纳模式对比(关键结论:差价是副产品,平台采购才是规模答案)**
| 模式 | 收入形式 | 首年收益 | 风险 |
|---|---|---|---|
| C1 纯转售 | 差价+缓存 | 净利 ¥3–6 万 | 转售合规 + 上游调价(2026 已涨 57214% |
| **C2 学校采购(推荐)** | 建设立项 ¥10–20 万 + 年度服务费 ¥3–10 万 + 竞赛按量 | **¥1535 万** | 项目制回款 |
| C3 混合 | 学校平台 + 学生增值付费 | 介于两者 | 定价需校批 |
C2 附带收益:合规消解(学校主体采购上游商用授权)、不垫资不担调价(按流水抽成/年费)、
软著+论文+奖项+校级平台经历。
**两条风险红线**:① 上游调价生死线 → model_pool 多供应商路由为生存设计;
② 学校自建私有化为最大替代 → 护城河 = 软件层(缓存整形/计费/交流文本/智能体平台),即毕设系统本身。
### 2.7 增补(2026-09-04):C 端客户端 + 商用批量采购对模型的修正
**两个新变量**:① llama.cpp 推理在 C 端——免费客户端(即本毕设端侧系统,捆绑 llama.cpp)
分发给学生,本地推理用学生硬件,学校本地层硬件成本归零;客户端**限制使用学校代理**
(学号登录换 key、不暴露 base_url、按 key 限流计费)。② 上游 key 走商用批量采购,
采购价为个人牌价 d 折(具体折扣商务洽谈,用敏感性覆盖)。
**统一 5 折敏感性矩阵**(毛利占收入比;r=1/30、o=0.8):
| 场景 | h₀ | d=1.0 | d=0.9 | d=0.8 | d=0.7 | d=0.6 |
|---|---|---|---|---|---|---|
| 保守 | 20% | 34% | 16% | 7% | +6% | +19% |
| 中性 | 40% | +12% | +21% | +30% | +39% | +47% |
| 考试周 | 55% | +41% | +49% | +53% | +59% | +65% |
盈亏平衡采购折扣:保守 d<7.5 折 / 中性 d<8.5 折 / 考试周 d<9.6 折。
**结论**:商用采购 ≤9 折时统一 5 折在中性场景 +21% 以上,可行;差异化定价(输入 5 折/输出 8 折)
降级为上游调价时的保险杠杆。5000 人年账(中性 d=0.8):商业净利约 3–5 万,学校采购(C2)仍为收益主体。
**C 端客户端的边界(诚实评估)**:llama.cpp 开源,技术上无法阻止学生自装直连——
锁定的是统一体验/计费合规/学校背书(默认通道),不是防破解。端侧缓存越强打代理流量越少,
对学校上游配额是省、对代理毛利中性偏负;客户端免费层能力边界(本地档位/上下文长度)
是与学校对齐的定价杠杆。商用合同以学校主体签订 → 转售合规与备案红线基本消解。
## 3. 与现有系统的复用映射
| 代理层需要 | 现有资产 | 改造量 |
|---|---|---|
| 多价位上游池 | `gateway/model_pool.py`local/budget/premium | 复用 |
| 语义缓存 | `router_system/cache.py`L1 精确 + L2 n-gram | 可选升 embedding |
| token 计量分账 | `V2Stats.by_model` | 补 cache_hit 维度 |
| 学生账户/配额 | `review.py` 的 sqlite 模式 | 新建 `billing.py` |
| 流式网关 | v3 SSE 基建 | 透传上游 SSE |
| 滥用兜底 | ReviewQueue 思路 + 限流 | 复用思想 |
## 3.5 技术选型与实现架构(实现 agent 按此执行,细化 §4)
**总原则**:不换语言、不加服务、不动 `router_system/`。被否选项:Go/Rust 独立服务(丢全部复用,
校园负载用不上)、Nginx/OpenResty+Lua(写不了 AI 感知逻辑)、Envoy/Kong/Cloudflare AI Gateway
(运维重/出内网)。**结论:Python 3.14 + FastAPI APIRouter 挂现有 app**——峰值 45K 请求/日
≈ 1.6 req/s、瞬时 3050 路流式,单进程 uvicorn 足够。
**请求链路**:鉴权(代理 key 哈希存储→令牌桶限流→余额熔断)→ 桶识别+规范化序列化 →
L0 两级缓存(精确哈希→语义向量 cosine≥0.92,命中即成本 0 照常计费)→ Singleflight 合并 →
前缀整形(canonical system+课程资料置顶、问题置末)→ model_pool 派发(local→budget→premium
首 token 前才可 failover)→ 流式 tee(转发+累积)→ 回写账本/缓存/指标。
**模块落点 `gateway/proxy/`6 模块)**
`auth.py`(key 签发/注销、令牌桶、日/并发上限)、`ledger.py`sqlite WALstudents/proxy_keys/
usage_ledger/semcache 四表;request_id 幂等;余额预扣-结算)、`normalizer.py`(桶识别、规范化、
前缀整形器)、`semcache.py`(两级缓存+singleflight+TTL/版本失效+int8 向量 LRU 30 万条≈300MB)、
`upstream.py`(派发+usage 归一化+流式转发)、`pricing.py`miss/hit/out 三价×峰谷系数;可注入时钟)。
**usage 归一化**DeepSeek `prompt_cache_hit_tokens`OpenAI 兼容 `prompt_tokens_details.cached_tokens`
Anthropic `cache_read_input_tokens`。流式须带 `stream_options.include_usage`;断连按已收 usage 计,
否则估算且不缓存。
**缓存感知技术清单(12 条)**:① 规范化序列化(稳定键序、剔易变字段)② 前缀钉扎(资料置顶问题置末
→ 上游 1/30 价命中)③ 桶作用域+版本失效(资料更新=版本+1)④ 两级缓存(复用 cache.py L1/L2 骨架,
L2 升级 embedding)⑤ 向量 int8+LRU ⑥ Singleflight 合并 ⑦ 空闲时段队列(非交互任务 off-peak 半价)
⑧ 前缀预热(max_tokens=1 廉价调用)⑨ 上游命中遥测回灌校准阈值 ⑩ L0 只缓存单轮(多轮靠上游前缀
自然命中,防上下文污染)⑪ 失败不缓存 ⑫ model_pool 补 hit_price 字段按命中价选上游。
**接入现有项目五步**:① `gateway/proxy/` 包 + `include_router`router_system 零改动(缓存算法
直接 import);② `config/settings.json``proxy` 段(桶/价格表/限流/allow_proxy),主 key 走 env
`webapp/src/views/ProxyView.vue` 三卡片(key 管理/用量查询/命中率-毛利看板)+ `npm run build`
`data/proxy.sqlite3`gitignore 已含 data/);⑤ 任务登记《任务拆解与执行计划.md》代理层节
T-P1 上游客户端+usage 归一化 / T-P2 鉴权账本 / T-P3 流式透传 / T-P4 整形器 / T-P5 语义缓存 /
T-P6 计价 / T-P7 前端页 / T-P8 200 并发压测),AGENTS.md 文档地图补一行;测试资源隔离清单追加
proxy.sqlite3。
**性能定案与逃生通道(2026-09-04 补充)**:代理为 I/O 密集型流式转发(重计算全部在 C 侧:
llama.cpp 推理/embedding、numpy 检索、sqlite、hashlib),Python 代理开销 13ms/请求,
占端到端延迟 <0.5%,峰值负载 <10% 单核——**确定用 Python**。硬性对策:向量检索必须
numpy/hnswlib 且限定课程桶内;embedding 必须走 llama-server 端点(禁止 torch 在代理内推理)。
**性能预算(T-P8 压测验收)**:代理附加 P99 ≤50ms、进程内存 ≤1GB、单核 ≥50 req/s
超标才启动数据面(透传+精确缓存)换 Go/Envoy 的局部手术——控制面/数据面分层 + 状态全外置
sqlite 保证该手术成本可控。减轻硬件消耗的优先级:缓存命中率设计 ≫ 本地模型量化 ≫ 空闲调度
≫ 代理语言(换 Go 仅省 ~150MB 内存)。
## 4. MVP 任务分解(供实现 agent 按序执行)
> **执行版已细化**:文件清单、DDL、接口签名、API 契约、逐任务验收见
> **《实施方案_代理层与缓存层.md》**(T-P0…T-P8,约 12 工作日)。本节 P1–P5 为摘要,以执行版为准。
> 约束:主 key 只从环境变量 / `config/settings.json` 读取(该文件已 gitignore),
> 任何代码、示例、测试中**不得出现真实凭据字面量**。
| # | 任务 | 验收 |
|---|---|---|
| P1 | 透传网关:`/proxy/v1/chat/completions`(OpenAI 兼容、流式透传) | curl 可用;流式与非流式均通 |
| P2 | 计量计费:解析上游 usage(含 `prompt_cache_hit_tokens`);sqlite 账本;欠额熔断 | 每请求成本/收入可查;欠额请求被拒 |
| P3 | 缓存栈:前缀整形器 + L0 语义缓存接入;命中率埋点 | 埋点输出 h_g、h_p、假想直连成本对比 |
| P4 | 账号配额:代理 key 签发/注销、额度、限流 | key 生命周期可管理;限流生效 |
| P5 | 看板:命中率/毛利/用量视图(复用 MetricsView 模式) | 三卡片可见 |
**端到端验收基线**:构造 ≥200 条校园模拟请求(重复/同义变体占 ≥50%),`h_g + h_p ≥ 50%`
在合理定价表下毛利为正。
## 5. 合规与风险(实现前必读)
1. **转售授权**:向上游 API 的转售/多租户使用可能受其服务条款限制,真实收费运营前必须确认
上游商用/分销政策;个人 key 转售存在封号风险。
2. **备案要求**:面向不特定公众提供生成式 AI 服务在国内需完成备案;校内限定人群合规负担小——
**从校内试点起步**
3. **主 key 安全**:仅存 env / `config/settings.json`;代码、文档、测试零字面量。
4. **缓存正确性**:语义缓存必须带 TTL 与失效策略,并按课程/时间分桶隔离——资料更新后旧答案
不能续用;作业场景"相似题 ≠ 可复用答案"。
5. **竞争**:学生自有 key、免费额度是替代品;定价锚定"便利性 + 稳定供给",不锚定成本。
## 6. 实验设计(论文/答辩素材)
- **E-P1 命中率-毛利曲线**:模拟 500 条校园请求(重复率 0% / 30% / 60% 三档),
测 h_g、h_p、混合成本、毛利率三组数字。
- **E-P2 前缀整形 A/B**:整形 vs 不整形的上游 `prompt_cache_hit_tokens` 对比——
证明"缓存钱"是设计出来的,不是碰运气。
- **E-P3 语义缓存质量**:L0 直答复用的正确性抽检(分桶/TTL 策略对照)。
**推理引擎选型(2026-09-04 定案)**:客户端(C 端)**llama.cpp,无悬念**——学生 Windows 本机
只有它能单文件零依赖运行(vLLM 仅 Linux + CUDA + torch 数 GB)。校端**默认 llama.cpp**
本地层峰值并发 1050 路,`--parallel` 足够;`--cache-reuse`/KV 量化与交流文本协议已耦合并经
E1 验证;与客户端同运行时同打包,不破坏"干净环境 20 分钟"验收。**vLLM = 条件启用的部署选项**,
不是替换——它说 OpenAI 兼容协议,启用即 model_pool 加一个端点条目(零代码)。触发条件
(三条同时满足):学校提供 Linux GPU 服务器(16GB+ 显存)+ 本地层持续并发 >50–100 路 +
本地层 token 占比成为吞吐瓶颈。推理引擎是可替换端点而非架构承诺;D1(只捆绑 llama.cpp
上游二进制)约束客户端分发件,不限制服务端池子的上游类型。
## 7. 与毕业设计主命题的关系
不改变已定命题《基于端云协同的编程智能体系统设计与实现》。本方向作为扩展章/答辩亮点:
**代理层 = 端侧基础设施的规模化形态**(从单机端侧到校园级端侧),核心贡献是
"缓存感知的 LLM 代理网关"——命中率和毛利曲线都是可量化、可复现的系统贡献。