同一个问题,代码、Jira 和 Confluence 各能回答到哪里
跨来源检索真正难的不是找到更多文本,而是判断需求、设计和当前代码分别能证明什么。
本页目录 · 9 节
「订单查询为什么没有返回这个字段?」
Jira 可能写着需求已经完成,Confluence 可能描述过设计,仓库里可能存在同名 Resolver,线上结果却仍然为空。四条材料可以同时为真,因为它们回答的是四个不同问题:团队想做什么、当时怎样设计、某个 Commit 实现了什么、某个环境实际运行了什么。
企业 RAG 最先要解决的不是召回率,而是来源权威边界。如果进入索引后只剩下一段匿名文本,系统越擅长总结,越容易把不同时间和证明能力的材料合成一段错误故事。
本篇知识地图
flowchart LR
Q["工程问题"] --> I["Intent<br/>Jira"]
Q --> D["Design<br/>Confluence"]
Q --> C["Implementation<br/>Pinned Git Commit"]
Q --> B["Business Config<br/>Read-only DDB Scope"]
I --> E["SourceRecord + Evidence Locator"]
D --> E
C --> E
B --> E
E --> V["按问题类型选择最终验证来源"]
这一章涉及五个核心知识点:Source Authority、Stable Identity、Metadata-only 与 Body-loaded 的区别、Temporal Semantics,以及最小权限采集。
当前接入的来源,不是四堆等价文本
mt_rag 当前本地数据范围有明确边界:
| 来源 | 当前采集范围 | 最适合证明 | 不能单独证明 |
|---|---|---|---|
| Jira | 受控项目范围内的 Issue、描述、评论、Changelog、Worklog、Remote Link、附件元数据与可读附件 | 需求、接受范围、状态变化和讨论 | 当前代码、部署版本、线上行为 |
| Confluence | 受控入口页及其 Descendants、多种 Body Format 与附件 | 设计背景、业务规则、术语和历史决定 | 当前调用链、某分支已实现 |
| Git/Bitbucket | 本机递归发现到的 Repository Inventory,逐仓更新到 main/master 后 Pin Commit,再解析代码 |
被 Pin Commit 中的静态实现、Symbol、Call Evidence | Bitbucket Workspace 全量覆盖、生产部署、运行时动态分派 |
| Business Config | 仅经单独确认的、非用户态只读配置范围 | 某次授权读取到的配置结构 | 用户态、交易与支付数据、实时生产行为 |
高成本或当前不支持的附件只保留 Metadata,不下载、不重试、不抽取文本。Business Config 明确排除任何用户态、交易态、支付态或可识别个人的数据;它仍然属于持久数据读取,必须经过单独的风险说明和显式确认,不能因为「RAG 需要更多数据」就自动执行。
PR 在目标合同中有独立 Source Type,但当前没有独立 PR Collector。图中的 PullRequest 主要来自 Jira/Confluence 文本中的 URL,是 link-derived entity;它不包含完整 Diff、Comments、Approvals 或 Revision。现有直接证据以 Pinned Code、Jira/Confluence Raw 和已采集 Remote Link 为准。合同允许一种来源,不等于当前 Corpus 已完整覆盖它。
SourceRecord:目标合同与现有采集产物之间仍有距离
SourceRecordV1 定义了希望所有来源最终遵守的严格合同。它不只保存正文,而是把一条来源记录拆成几个互不替代的域:
record_id / source_type / native_id
record_payload_hash
content_status
original_blob + original_content_hash
created_at / updated_at / observed_at
source_url / attachment_urls
freshness / metadata / provenance
这里最重要的是 content_status 的 Fail-closed 语义:
available必须同时拥有 Original Blob 与 Original Content Hash;metadata_only和unavailable不允许假装自己有正文;- 下游看到一个页面标题和 URL,只能说「找到了这个页面」,不能总结页面内容。
但不能把目标合同写成已经完成的迁移。当前生产 Collector 主要仍输出 Legacy Raw JSON 和 SourceManifestEntryV1,SourceRecordV1 的直接构造更多出现在 Legacy Adapter 与测试中。查询侧已经执行同方向的 Fail-closed 约束:Confluence Result 只有 document_read_status=body_loaded 才能进入业务 Summary;metadata_only 或 body_missing 只能作为定位线索。这里的真实状态是「下游合同已经收紧,上游格式仍在迁移」。
稳定身份不能用标题
页面会改名,Issue 状态会变,代码行号会移动。稳定 ID 必须由来源的 Native Identity 构造,而不是由显示文本构造:
| 对象 | 稳定身份 |
|---|---|
| Confluence Page | con_<content_id> |
| Jira Issue | jira_<ISSUE_KEY> |
| Repository | Workspace + Repo Slug |
| Attachment | Parent Source Record + Native Attachment ID |
| Code Symbol(目标 Helper) | Repo、Language、Relative Path、Kind、Qualified Name |
目标 code_symbol_id() 不包含 Commit 和 Line Range。原因是它表达「逻辑上的同一个 Symbol」;Commit、Line 和内容 Hash 属于这一版证据的 Revision/Locator。反过来,如果把 Commit 塞进实体 ID,每次代码更新都会制造一个全新实体,图上就无法表达同一个 Symbol 的版本变化。
这里还有一个尚未收敛的实现缺口:实际 CPG _node_id() 目前把 Line 编入 Hash,Symbol 仅移动行号就会改变 Node ID。现有 Stable-ID 测试覆盖的是 Helper,并不能证明真实 CPG Node Identity 已跨行移动稳定。文章保留这一差异,因为它正是 Canonical Migration 需要继续解决的问题。
同一个字段问题,正确阅读顺序是什么
假设要判断一个 GraphQL Field 是否已经贯通:
- 从 Jira 确认字段是否在接受范围内,而不是只在评论中被提过;
- 从 Confluence 理解字段的业务语义和当时的方案,但记录页面版本与更新时间;
- 从目标仓库的 Pinned Commit 定位 Operation、Selected Field、Resolver 和 Provider;
- 只有出现
USES_OPERATION -> SELECTS_FIELD -> RESOLVES_FIELD等带 Evidence 的 Graph Edge,才把关系写成已验证调用链; - 如果问题问「生产是否生效」,还要另查 Deployment/Runtime Readback。当前静态 Corpus 到这里必须停下。
这不是固定的「Jira 优先级低于代码」。优先级取决于问题:问需求就以 Jira/设计为主,问实现就以当前 Commit 为主,问生产状态则这些静态来源都不够。
Code Source 为什么要先 Pin
代码采集的核心不是「扫描某个目录」,而是建立可重复读取的 Revision。
当前 Source Gate 会先递归发现代码仓库,并通过显式 Denylist 排除历史工作区副本;更新前对所有仓库做 tracked-change Preflight,发现冲突就全局阻断,不 Reset、Clean 或 Stash 用户改动。然后逐仓执行 fetch --prune、解析 origin/HEAD 或唯一可用的 main/master、pull --ff-only,验证 HEAD == origin/<branch> 后记录 Commit Pin。
解析阶段会再次验证 Pin,再从已验证 Working Tree 路径读取字节并把 Commit 写进 Locator;它不是逐文件执行 git show <commit>:<path>。因此正常情况下 Working Tree 与 Pin 对齐,但验证和实际读取之间仍存在较小的 TOCTOU 窗口。网络失败时,显式 Degraded Mode 只允许 Pin 一个干净且恰好等于缓存 Remote Ref 的分支,并标成 stale_remote_unreachable,不会声称 Fresh。
失败模式与取舍
| 失败模式 | 保护策略 | 取舍 |
|---|---|---|
| 页面只有标题,没有正文 | metadata_only,禁止正文总结 |
少给一个答案,也不根据标题猜业务规则 |
| Jira 状态为 Done | 只证明工单状态,不能映射成部署成功 | 需要额外 CI/部署/运行时证据 |
| 仓库 Dirty 或无法 Fast-forward | 本轮 Code Extraction Fail Closed | Freshness 让位于不覆盖开发者工作和可重复性 |
| Remote 不可达 | 可用缓存 Pin 只能标 Stale | 能定位旧入口,但不能宣称当前 |
| 增量采集里没看到某对象 | 不把「缺席」当删除 | 真删除要等权威 Full Inventory/Reconciliation |
| DDB 范围不清楚 | 默认不执行;仅允许固定 Read-only Business Config Scope | 少收数据以换取隐私、成本与生产安全 |
| Scheduled Jira Handoff | 当前定时命令先完整 Collect,再只 Enrich Placeholder;可能没有 Jira Issue Source Event | Raw 已更新不等于 V3 Journal 已闭环,需要补事件验收 |
| 旧 Full Refresh 入口读取 DDB | 兼容路径不自动继承 Weekly Gate | 仍需外部安全确认,不能把 --read-only 当零风险 |
可复核的项目证据
- Source Policy 配置:受控文档入口、Issue 范围、Code Source Gate 和 Business Config Include/Exclude Scope;
src/mt_rag/core/system_contracts.py:SourceRecordV1、EvidenceRefV1和content_status不变量;src/mt_rag/operations/cli.py:Jira/Confluence Collector、附件、Raw 写入与当前 Source Event Handoff;src/mt_rag/code/repositories.py:Repo Discovery、Inventory Comparison、Dirty Preflight、Branch Resolution 与 Pin;src/mt_rag/documents/jira.py:Description、Comments、Changelog、Worklog、Remote Link 转成可定位 Block;src/mt_rag/documents/batch.py:Confluence Raw 到 DocumentIR 与 CAS;README.md的「当前数据范围」与「可重入策略」:实际 Corpus、附件与 DDB 边界。
参考资料
- Atlassian Jira Cloud REST API 官方文档;
- Atlassian Confluence Cloud REST API 官方文档;
- Bitbucket Cloud Repositories API 官方文档;
- Git Internals:Git Objects;
- AWS DynamoDB 读取一致性官方文档。
跨来源 RAG 的第一条原则不是「接得越多越好」,而是每条材料进入系统后仍然知道自己是谁、来自哪一版,以及它最多能证明到哪里。
相关文章
RAG 不是一条必经流水线,而是一种按需取证的能力
先理解目标、先看当前源码,只有跨来源信息真的能改善判断时,才调用 Local RAG 去找证据候选。
一次代码命中为什么还不算答案
工程检索必须把结果绑到仓库、commit 和 symbol 上;缺失、过期或来自错误仓库的命中,只能当定位线索。