一、项目背景与痛点

上个月接了个私活,给本地一家连锁宠物医院做智能预诊 + 电子病历 + 宠物档案管理系统。核心流程很简单:

用户端输入宠物症状 → AI 生成预诊报告 → 医生后台审核确认 → 形成结构化病历 → 归档便于后续随访。

但做到 AI 接入层时,遇到了几个很现实的问题:

  1. 网络问题:直接调 OpenAI/Claude 官方接口需要代理,公司内网环境挂代理连开发机都费劲,频繁超时。

  2. 计费不透明:官方按美元扣费,汇率波动,且没有细粒度的额度管控,测试阶段心里完全没底。

  3. 多模型管理麻烦:预诊用 Claude 3.5,轻量任务用 DeepSeek,兜底用 GPT-4o-mini,每个模型都要单独管理 Key 和 Endpoint,代码里一堆 if-else

  4. 成本不可控:开发测试阶段疯狂调接口,几天就能烧掉几十美元,私活预算扛不住。

二、API 接入方案选型

针对上述问题,我梳理了三种技术方案:

方案 优点 缺点 适用场景
官方 API 直连 官方保障,文档齐全 需代理、延迟高、美元计费、多模型管理复杂 有稳定海外网络环境的企业
自建反向代理 完全可控、无中间商 维护成本高、需解决线路稳定性、证书、限流等问题 有运维资源的中大型团队
第三方中转网关 国内直连、统一接口、人民币计费、额度管控 多一层依赖、文档/调试能力参差不齐 独立开发者、中小团队、快速验证 MVP

最终选择了第三方中转网关方案。原因很实际:这是私活项目,工期紧、预算有限,没有精力自建代理。在对比了几家国内中转服务后,选用了 Aegisy(官网:https://www.aegisy.top/),主要看中了国内直连延迟低、支持多模型统一接口、可按人民币充值并设置额度预警。

用户注册可以得到试用额度,前期可以直接写代码测试,对新用户相对比较友好

这里不展开具体品牌对比,只分享选型逻辑:选中转网关时,建议重点考察三点——线路稳定性(国内直连延迟)接口标准度(是否兼容 OpenAI 格式)成本可控性(是否支持额度限制和预警)

三、系统架构设计

技术栈是标准的 Java Web 组合:

  • 后端:Spring Boot 3.x + MyBatis-Plus + PostgreSQL(pgvector 扩展做向量检索)

  • AI 接入层:统一 HTTP Client 对接中转网关,内部做模型路由

  • 前端:Vue3 + Element Plus

  • 部署:Docker Compose,单机 2C4G 云服务器

3.1 核心模块划分

  1. 智能预诊模块

    • 接收用户输入的症状描述

    • 调用 AI 生成预诊报告(包含疑似病症、建议检查项、用药参考)

    • 医生后台审核确认后方可入库

  2. 病历管理模块

    • 标准 CRUD,但病历内容采用结构化 JSON 存储(诊断、用药、医嘱、随访计划)

    • 支持按宠物档案维度检索历史病历

  3. 知识库问答模块(RAG)

    • 基于宠物医疗 FAQ 和药品说明书构建向量库

    • 用户提问时先检索相关知识片段,再拼接进 Prompt 做约束生成

    • 向量库直接用 PostgreSQL 的 pgvector 扩展,避免额外引入 Milvus/Pinecone

3.2 AI 接入层架构

为了不把网关地址和 Key 写死在业务代码里,我封装了一个统一的 AI Service:

@Service
public class AiDiagnosisService {

    @Value("${ai.gateway.base-url}")
    private String baseUrl;

    @Value("${ai.gateway.api-key}")
    private String apiKey;

    private final WebClient webClient;

    public AiDiagnosisService(WebClient.Builder builder) {
        this.webClient = builder
            .baseUrl(baseUrl)
            .defaultHeader(HttpHeaders.AUTHORIZATION, "Bearer " + apiKey)
            .build();
    }

    /**
     * 流式预诊:使用 SSE 实时推送给前端
     */
    public Flux<String> streamDiagnosis(DiagnosisRequest request) {
        String prompt = buildMedicalPrompt(request);
        
        Map<String, Object> body = Map.of(
            "model", routeModel(request.getComplexity()), // 根据复杂度路由模型
            "messages", List.of(Map.of("role", "user", "content", prompt)),
            "stream", true,
            "temperature", 0.3 // 医疗场景降低随机性
        );

        return webClient.post()
            .uri("/v1/chat/completions")
            .bodyValue(body)
            .retrieve()
            .bodyToFlux(String.class)
            .onErrorResume(e -> {
                log.error("AI 调用异常", e);
                return Flux.just("{\"error\":\"预诊服务暂不可用,请稍后重试\"}");
            });
    }

    /**
     * 模型路由策略:
     * - SIMPLE: DeepSeek-V3(关键词提取、症状分词)
     * - COMPLEX: Claude 3.5 Sonnet(预诊报告生成)
     * - FALLBACK: GPT-4o-mini(兜底)
     */
    private String routeModel(Complexity level) {
        return switch (level) {
            case SIMPLE -> "deepseek-chat";
            case COMPLEX -> "claude-3-5-sonnet-20241022";
            default -> "gpt-4o-mini";
        };
    }
}

3.3 成本控制机制

中转网关控制台支持单 Key 额度限制IP 白名单,我在系统里做了两层防护:

  1. 网关层:设置 Key 的日消费上限(50元),超出自动失效,防止测试阶段被刷。

  2. 应用层:对 AI 调用做滑动窗口限流(RateLimiter),单个用户每分钟最多 3 次预诊请求。

// 应用层限流示例(基于 Guava RateLimiter)
@PreAuthorize("hasRole('USER')")
@PostMapping("/diagnosis/stream")
public Flux<String> diagnosis(@RequestBody @Valid DiagnosisRequest req) {
    if (!diagnosisRateLimiter.tryAcquire()) {
        return Flux.just("{\"error\":\"请求过于频繁,请稍后再试\"}");
    }
    return aiDiagnosisService.streamDiagnosis(req);
}

四、踩坑记录与解决方案

4.1 文档边缘场景覆盖不足

官方文档对常规调用说明比较清楚,但错误码和异常场景描述不够细致。例如:

  • 当模型触发 content_filter 时,返回的 JSON 结构没有明确示例

  • 流式输出中途断连时,客户端重连策略文档只提了一句,没有代码参考

  • 多轮对话中 system 角色的权重和长度限制说明缺失

解决方案:自己用 Wireshark + 日志拦截器抓包,整理了一份内部的《异常码速查表》。建议大家在生产环境接入任何第三方 AI 网关时,都先跑一轮完整的异常 case 测试。

4.2 调试链路不透明

医疗系统对可观测性要求高,我需要知道每次 AI 调用的完整耗时、Token 消耗、是否重试。但网关返回的响应头里只有基础耗时,缺少上游模型的详细 trace。

解决方案:在 WebClient 外包了一层 OkHttp 拦截器,自己打日志:

client.addInterceptor(chain -> {
    Request request = chain.request();
    long startNs = System.nanoTime();
    Response response = chain.proceed(request);
    long tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs);
    
    log.info("[AI-Gateway] {} {} | {}ms | model={}",
        request.method(),
        request.url(),
        tookMs,
        request.header("X-Target-Model"));
    
    return response;
});

4.3 额度监控延迟

控制台的费用统计有 5-10 分钟延迟。开发阶段高频调试时,额度掉了但界面没刷新,容易造成误判。

建议:不要完全依赖控制台做实时预算判断,应用层自己维护一个调用次数和 Token 数的本地计数器,做粗粒度预估。

4.4 长文本上下文截断

宠物病历包含既往病史、过敏史、长期用药记录,上下文很容易超过 8k Token。测试时发现偶尔会出现内容截断或总结性压缩,导致返回的病历不完整。

解决方案:放弃"一次性塞全文"的策略,改用分段 RAG 检索

  1. 先将长病历按段落切分,做 Embedding 存入 pgvector

  2. 用户提问时,检索 Top-3 相关片段

  3. 只将相关片段 + 当前症状拼接进 Prompt,控制单次调用在 4k Token 以内

-- pgvector 相似度检索示例
SELECT content, embedding <=> query_embedding AS distance
FROM pet_medical_chunks
WHERE pet_id = ?
ORDER BY embedding <=> query_embedding
LIMIT 3;

五、运行数据与效果

系统上线前做了压测,数据如下:

指标 结果
平均响应时间 2.3s(含 AI 生成)
流式首字延迟 800ms
单日最大调用量 1200 次
开发+测试周期总费用 ¥186
预诊报告可用率 75%(25% 需医生修正,符合预期)

医生端反馈:对于皮肤病、肠胃炎、呼吸道感染这三类高频病例,AI 预诊能节省约 30% 的初诊时间,给出的用药建议框架基本可用。但涉及复杂慢性病和罕见病时,仍需医生主导判断。

六、总结与建议

6.1 中转网关适合什么场景?

经过这个项目的实践,我认为第三方 AI 中转网关比较适合:

  • 国内独立开发者/小团队:不想折腾代理和海外支付,快速验证 AI 功能

  • 多模型混合策略:一个项目里要根据任务类型切换不同模型,统一接口能减少很多胶水代码

  • 预算敏感型项目:需要人民币计费和额度管控,防止测试阶段费用失控

不适合的场景:

  • 对延迟要求极高的实时语音/视频交互(建议直接用官方或自建专线)

  • 需要完整审计日志和合规追溯的金融/医疗核心系统(中转网关作为接入层没问题,但必须在上面再包一层自己的网关做日志留存)

6.2 给同类项目的建议

  1. 不要裸调第三方网关:务必在应用层包一层自己的限流、降级、日志拦截器。

  2. 医疗场景一定要降低 Temperature:我设的是 0.3,防止 AI 胡编乱造药品剂量。

  3. 长文本不要用大上下文硬塞:RAG 检索 + 片段拼接,比直接扔 8k Token 更稳。

  4. 先小规模验证再放量:任何第三方服务,都建议先用免费额度或小额充值跑通核心流程,确认延迟和稳定性符合预期后,再投入生产

推荐阅读标签: #AI编程 #GPT-5.5 #Claude4 #API中转 #Cursor #ClaudeCode #CodexCLI #大模型API #AI写代码 #开发者工具 #Token计费 #开源模型 #MCP协议 #A2A协议 #AIAgent #智能体 #流式输出 #函数调用 #长上下文 #降本增效 #算力成本 #提示词工程 #OpenAI兼容 #多模态大模型 #推理模型 #LLMAPI #API代理 #AI网关

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐