如何构建自己的代码智能层
RAG、安全分析和测试智能——统一在单个API后面,任何IDE代理都可以调用。
AI模型价格对比 | AI工具导航 | ONNX模型库 | Vibe Coding教程 | PLC在线仿真器 | Tripo 3D | Meshy AI | ElevenLabs | KlingAI | ArtSpace | Phot.AI | InVideo
构建一个能够推理你的代码库的AI助手听起来很简单——直到你在支付领域工作。
你有合规边界。多个服务带有长期存在的分支。安全策略意味着你不能随意将源代码管道传输到第三方API。而且团队中的架构师、安全工程师和QA负责人都有完全不同的问题。
通用的"与代码聊天"工具在这种环境中很快就会失效。
我们需要不同的东西:一个完全由我们控制的后端,限定在我们的服务和我们的分支范围内,具有不同的模式用于架构推理、安全审查和QA。这篇文章诚实地讲述了我们构建了什么、背后的设计决策,以及我们会做出哪些不同的选择。
1、现有工具的问题
大多数IDE AI集成做出了三个在支付平台中不成立的假设:
假设1:你只有一个仓库
假设2:main分支是最新的
假设3:你愿意将源代码发送到云端推理端点
假设4:模型会在不知道时说"我不知道"
这些对我们来说都不成立。实际情况是:
多仓库复杂性。 我们平台中的单个用户旅程至少涉及API网关、交易服务、银行API和通知层。只了解一个仓库的助手会给出关于跨链影响的浅薄且通常错误的答案。
分支现实。 我们运行长期存在的develop和发布分支。假设main是规范代码库的助手会自信地描述实际上没有实时环境在运行的代码。
合规约束。 将原始源代码——尤其是任何接近卡数据流或认证逻辑的代码——发送到外部推理端点需要法律审查,我们不想被此阻塞。我们需要本地推理作为一等选项,而不是事后考虑。
孤岛式工作流。 安全工程师询问认证流程需要与架构师询问影响范围或QA工程师询问测试覆盖率差距不同的深度。单一的"询问代码库"按钮产生的答案对每个人来说都是平庸的。
幻觉风险。 当你的实际代码不在上下文中时,通用助手会从训练数据中回答。在支付领域,关于你的认证流程、重试逻辑或卡数据路径的自信的错误答案不是小麻烦——而是责任。"我不知道"在证据缺失时是正确的答案。大多数工具不会这么说。
2、我们构建了什么
CORTEX AI 是一个完全运行在我们控制的基础设施上的FastAPI服务。它将代码摄取到图数据库(结构和依赖关系)和向量存储(语义检索)中,然后在/api/v1下暴露一个干净的HTTP API。
在该API之上,我们提供了一个薄桥接层——一个无状态适配器,任何支持MCP的AI代理都可以将其作为子进程启动。
╔═══════════════════════════════════════════════════════════════╗
║ 整体架构示意图 ║
╠═══════════════════════════════════════════════════════════════╣
║ ║
║ ┌─────────────────┐ tool calls (stdio) ┌─────────────┐ ║
║ │ │ ◄────────────────────► │ 薄桥接层 │ ║
║ │ IDE / 代理 │ │ (无状态) │ ║
║ │ (任意AI宿主) │ └──────┬──────┘ ║
║ └─────────────────┘ │ HTTPS ║
║ ▼ ║
║ ┌─────────────────┐ ║
║ │ CORTEX 后端 │ ║
║ │ FastAPI /v1 │ ║
║ └────────┬────────┘ ║
║ │ ║
║ ┌────────────────────────┤ ║
║ │ │ ║
║ ┌──────▼──────┐ ┌────────▼──────┐ ║
║ │ 图数据库 │ │ 向量存储 │ ║
║ │ (结构+依赖) │ │ (语义检索) │ ║
║ └─────────────┘ └───────────────┘ ║
╚═══════════════════════════════════════════════════════════════╝
关键设计原则: 桥接层故意保持简单。它转发工具调用并返回响应。没有业务逻辑,没有状态。这很重要,因为你可以完全独立于任何IDE或代理运行时测试、基准测试和curlAPI——使得编写集成测试和调试生产问题变得容易得多。
设计上快速失败。 后端在启动时验证图数据库连接。如果图数据库宕机,服务拒绝启动。一个数据密集型服务如果静默降级会给你自信的错误答案——这比没有答案更糟糕。
3、三条分析通道
最大的设计决策是拒绝构建单一的"询问你的代码库"端点。
以下是为什么这在支付上下文中很重要:
同样的问题。不同的上下文。完全不同的有用答案。
"当支付处理器重试时会发生什么?"
→ 架构师需要:跨服务的爆炸半径、依赖图
→ 安全需要:幂等性风险、重放攻击面
→ QA需要:需要覆盖的边界情况、测试桩建议
将这些混入一个提示模式中会产生对每个人来说都平庸的答案。
我们将后端构建为三条明确的通道——通过不同的工具名称和端点来选择,而不是通过提示措辞。这使得行为可预测和可审计:你可以查看日志并确切知道哪条分析路径运行了。
3.1 通道1:架构与影响推理
┌────────────────────────────────────────────────────┐
│ "取消操作如何在账本中流动?" │
└────────────────────┬───────────────────────────────┘
│
▼
图遍历(显式依赖)
│
▼
语义检索(代码上下文)
│
▼
带依赖链的叙述性答案
处理影响分析、跨服务追踪和"如果我改变X会破坏什么?"类问题。此通道首先使用图追踪显式依赖边,然后拉取代码上下文以生成叙述性答案。它是通用代码库问题的默认模式。
3.2 通道2:安全聚焦分析
┌──────────────────────────────────────────────────────┐
│ 输入:问题 + 可选 changed_files_json │
│ + 可选依赖清单 │
└──────────────────────┬───────────────────────────────┘
│
┌───────────┴────────────┐
▼ ▼
认证/注入路径 清单扫描
秘密信息检查 (CVE暴露面)
安全卫生审查
│ │
└───────────┬────────────┘
▼
结构化发现 + 修复建议
(保守策略:说"我不知道"
而不是猜测)
处理认证流程、注入路径、秘密信息卫生和差异范围审查。接受已更改文件列表,使代理可以限定"审查此PR的安全影响",而不会幻觉出它未看到的差异。故意保守——如果模型没有足够的上下文,它会如实说明。
3.3 通道3:测试与QA智能
┌──────────────────────────────────────────────────────┐
│ 输入:普通问题 / 测试缺口 / API错误JSON │
└──────────────────────┬───────────────────────────────┘
│
▼
结构化输出:
┌─────────────────┐ ┌──────────────────┐
│ test_plan │ │ qa_error_insight │
│ (机器可读) │ │ (根因 + 复现步骤) │
└────────┬────────┘ └──────────────────┘
│
▼
下一个代理步骤转换
test_plan → 测试桩
(无需额外轮次)
返回机器可解析的输出——后续代理步骤可以直接将test_plan转换为测试桩,无需另一次LLM调用。这种可组合性使其在CI工作流中真正有用,而不仅仅是聊天演示。
4、检索架构
RAG的效果取决于你的索引策略。以下是我们通过艰难方式学到的一些东西:
4.1 分支感知索引
❌ 大多数工具的做法:
payment-service ──► 单一索引(假设为main)
✅ 我们的做法:
payment-service@develop ──► 索引命名空间A
payment-service@release/2.4 ──► 索引命名空间B
payment-service@main ──► 索引命名空间C
代理被指示——无论是在系统提示中还是在工具文档中——永远不要假设分支名称。它们首先调用list-repos工具;分支名称来自API。这一个改变消除了整个类别的自信但错误答案。
4.2 仅检索 vs. 服务端回答查询
┌── cortex_retrieve ──┐
用户问题 ──► │ 返回:原始 │──► IDE模型编写答案
│ 证据块 │ (留在你的基础设施上)
└──────────────────────┘
┌── cortex_query ─────┐
用户问题 ──► │ 发送证据到 │──► 完整结构化响应
(安全/QA通道) │ 服务端模型 │ (强制输出格式)
└─────────────────────┘
我们有意暴露两条路径。cortex_retrieve返回原始证据——匹配的代码、图路径、依赖边——并让IDE的模型编写最终叙述。此路径使答案完全保持在客户控制的推理范围内,这对合规很重要,且成本更低。大多数日常问题通过仅检索路径处理;服务端回答路径保留给安全QA通道,这些通道需要强制结构化输出格式。
4.3 图 + 向量:为什么两者都需要
向量搜索单独: 图单独:
┌──────┐ 相似 ┌──────┐ ┌──────┐ ──调用──► ┌──────┐
│ A │ ◄──────► │ B │ │ A │ │ B │
└──────┘ └──────┘ └──────┘ └──────┘
找到语义相似的块 找到结构关系
——但不知道A调用B ——但错过了代码的语义上下文
两者结合:
"找到所有传递调用欺诈检查端点的服务"
→ 图遍历提供结构链
→ 向量检索用相关代码上下文丰富每一跳
→ 答案在结构上有依据且上下文丰富
向量搜索找到看起来相似的东西。图追踪实际调用的关系。在"服务A是否有通往卡库的路径?"是一个真实安全问题的支付平台中,你需要两者。
5、如何消除幻觉:从架构上,而不是通过提示
代码感知AI工具中的幻觉不是一个你可以通过祈祷和提示来解决的模型质量问题。这是一个架构问题。如果代理没有依据,它会编造听起来合理的答案。解决方法是消除允许编造的条件。
以下是幻觉进入典型代码助手的地方,以及我们在每个点上所做的事情:
┌──────────────────────────────────────────────────────────────────────────┐
│ 幻觉从哪里进入(以及我们如何阻止它们) │
├──────────────────────┬──────────────────────┬───────────────────────────┤
│ 幻觉类型 │ 发生方式 │ 我们的结构性修复 │
├──────────────────────┼──────────────────────┼───────────────────────────┤
│ 错误分支 │ 代理假设为main │ 分支名称仅来自API │
│ │ 或从记忆中编造分支 │ 代理必须先调用list-repos │
├──────────────────────┼──────────────────────┼───────────────────────────┤
│ 编造的代码路径 │ 代理推断听起来 │ 图遍历返回实际边 │
│ │ 合理的调用链 │ 不允许推测 │
├──────────────────────┼──────────────────────┼───────────────────────────┤
│ 过时上下文 │ 代理从训练截断 │ 结果返回时附带 │
│ │ 而非代码库回答 │ 索引时间戳 │
│ │ │ 代理会展示这一点 │
├──────────────────────┼──────────────────────┼───────────────────────────┤
│ 幻觉出的差异 │ 安全审查编造PR中 │ 更改文件必须显式传递 │
│ │ 的变更内容 │ 为JSON负载。无差异 │
│ │ │ = 不尝试审查 │
├──────────────────────┼──────────────────────┼───────────────────────────┤
│ CVE编造 │ 代理仅根据包名 │ CVE发现仅在附加并 │
│ │ 猜测漏洞 │ 扫描清单时才呈现 │
└──────────────────────┴──────────────────────┴───────────────────────────┘
所有这些的主题都是一样的:如果代理没有得到事实,它就无法回答。工具契约强制执行这一点。如果所需的负载不存在,工具会返回错误,而不是让模型用编造来填补空白。
5.1 "证据优先"的答案流程
❌ 没有依据(典型聊天助手):
问题 ──► LLM (训练数据 + 上下文窗口) ──► 答案
↑
用合理的编造填补空白
✅ 使用CORTEX(证据优先):
问题
│
▼
图遍历 ←── 实际的服务依赖边
│
▼
向量检索 ←── 从你的仓库索引的实际代码块
│
▼
证据包 ←── 只包含找到的内容;缺口就是缺口,不是猜测
│
▼
LLM叙述 ←── 模型在证据之上编写答案,而不是替代证据
│
▼
答案(附带源引用)
我们在安全通道中强制执行的一个特定行为最尖锐地说明了这一点:如果问了一个安全问题但相关服务尚未被索引,工具返回**"服务未索引——先索引,然后重试"**,而不是让模型从训练数据中回答。在支付安全上下文中,关于你的认证流程的自信错误回答是主动危险的。沉默才是正确的响应。
5.2 幻觉 vs. 不确定性:教代理说"我不知道"
大多数代理框架推动模型给出答案。我们则推动在证据薄弱时明确表达不确定性:
证据覆盖率 代理行为
───────────────────── ──────────────────────────────────────────────
高(>80% 已检索) 完整答案,附带内联源引用
中(40–80%) 部分答案 + 标记缺口 + 索引建议
低(<40%) "覆盖不足——请先索引这些服务"
无(未索引) 硬停止——工具返回错误,不进行LLM调用
这个阶梯是观察到工程师相信中等置信度答案但结果却是半编造的结果。"部分答案 + 标记缺口"层级是专门为阻止这种情况而添加的。
6、代理实际能做什么
工具表面直接映射到内部API能力。这是我们最坚持的原则:你的工具列表应该看起来像一个内部API,而不是一个提示菜单。
┌──────────────────────────────────────────────────────────────┐
│ 工具表面 │
├────────────────────┬────────────────────────────────────────┤
│ 健康与库存 │ 服务可达吗?索引了什么? │
│ │ 哪些分支?→ 总是先调用 │
├────────────────────┼────────────────────────────────────────┤
│ 索引 │ 单仓库 / 批量 / 全分支 │
│ │ → 过时索引上的RAG = 过时答案 │
├────────────────────┼────────────────────────────────────────┤
│ 查询 │ 服务端回答或仅检索 │
│ │ 两者都限定到仓库+分支 │
├────────────────────┼────────────────────────────────────────┤
│ 安全 │ 差异感知、清单感知审查 │
│ │ 接受changed_files_json用于PR范围 │
├────────────────────┼────────────────────────────────────────┤
│ 测试智能 │ 结构化test_plan + qa_error_insight │
│ │ 机器可读 → 可组合管道 │
└────────────────────┴────────────────────────────────────────┘
我们在单独的使用指南中记录了示例代理提示。结果证明这比预期的更有价值——它为团队提供了共享词汇表,并弥合了"在演示中有效"和"在周一早上有效"之间的差距。
7、我们会做出哪些不同的选择
从仅检索路径开始。 我们首先构建了服务端回答的查询,因为它产生了更令人印象深刻的演示。在生产中,仅检索覆盖了大多数问题,成本更低,并且引发的合规问题更少。先把令人印象深刻的东西放在第二位。
尽早投资工具描述文本。 AI代理基于描述字符串决定调用哪些工具。模糊的描述会导致错误的工具选择。我们花了整整两次迭代重写工具描述,直到代理行为变得一致正确。把它们当作API文档来对待——版本化、审查、不是事后才想到的。
分支意识既是文化问题也是技术问题。 即使API强制执行分支范围的查询,工程师有时仍然像只存在一个规范代码库一样提示代理。解决方法不是更多代码——而是在示例提示库中添加显式的分支确认步骤,并使list-repos调用在代理输出中可见,以便工程师能够看到实际索引了哪个分支。
8、这种模式适合谁
适合 ✓ 不适合 ✗
───────────────────────────────── ──────────────────────────────────
多个服务的版本控制需要 单仓库,无合规需求
跨仓库RAG → 简单的托管工具就够了
源代码合规/数据驻留限制 没有基础设施所有权的团队
不同用户角色需要不同的 → 供应商产品更容易
答案格式 原型/概念验证
想要拥有集成面的团队 → 对当前阶段来说过度设计
这是为受限场景做的工程。如果你是一个只有一个仓库且没有合规要求的独立开发者,不要构建这个。
9、下一步计划
为图提取层提供超越Java的更广泛语言支持。一个强化部署手册,涵盖TLS终止、令牌轮换和向量存储的网络出口规则。以及关于各个通道的更专注的专题文章——尤其是安全分析路径,其深度足够单独写一篇文章。
如果你正在构建类似的东西——内部服务的RAG、代理可调用的分析管道、受监管环境的MCP风格工具面——我们有兴趣交流心得。这些模式仍在形成中,关于如何在这样的设置中门控工具、管理秘密或构建多模型路由,还没有明显的社区共识。
原文链接:How We Built a Code Intelligence Layer That Actually Understands Our Payment Platform
汇智网翻译整理,转载请标明出处