P1 AIP
OAG 对象路:对实体对象查数、操作
OAG(Ontology-Aware Generation,对象路)让 AIP 不只对着裸表查数,而是面向"本体对象":查数时把 Foundry 里的指标口径公式、对象、属性结构化注入 Text2SQL prompt;操作时经动作代理端点把对象动作(validate / execute)透传给 Foundry 执行。2026-08-10 验收 5/5 PASS。看完这 3 个故事,你就懂"对象查询 + 对象操作"是怎么一条路走通的。
业务分析师
数据工程师
IT 管理员
OAG
本体
动作代理
共 3 个故事
能 / 不能速览
✅ 这个主题能做
- NLQ 查数时 OAG 对象路优先命中:调 Foundry 语义检索,把指标口径公式 + 对象 + 属性结构化注入 Text2SQL
- 对象操作代理:POST /oag/actions/:name/validate(dry run)与 /execute(执行),透传 Foundry 契约
- 异步动作状态查询:GET /oag/actions/runs/:id(ASYNC 模式轮询)
- OAG 任一环节失败 / 超时 / 403 自动降级本地本体 → RAG 关键词路径,演示不中断
- 对象数 / 属性 / 指标 / token 预算裁剪:默认对象 20、每对象属性 8、指标 5、token 预算 2000
⛔ 这个主题做不了
- OAG 默认关闭(foundry.oag.enabled=false),需配置 Foundry base_url 且对象级安全就绪才启用
- OAG 未启用时动作代理端点返回明确 404(能力未装配,不是路径写错)
- AIP 只做契约代理零写路径逻辑:业务成败判定(VALID)与幂等重放(REPLAYED)语义都在 Foundry
- 动作执行缺 idempotency_key(运行类模式)时 Foundry 返回 400,AIP 只透传
- 跨产品用户库问题:AIP 用户 UUID 在 Foundry 不存在时可能 403,需配置服务账号 token(foundry.oag.service_token)
适用角色
本主题面向两个角色:
- 业务分析师:OAG 的受益者——问"各产品销售额"时拿到的是 Foundry 里的"正口径"指标,比对着裸表问更准。
- 数据工程师 / IT 管理员:在 Foundry 侧维护对象 / 指标 / 动作,配置 OAG 开关与列名映射,让对象路跑起来。
对象与指标的定义在 Foundry(P2)维护,AIP 通过 OAG 契约消费,两个产品在这里打通。
能力速览(能做什么)
对象路检索注入
BuildOAGContext 调 Foundry 语义检索(scopes=metric/object/property),按对象归组、裁剪、列名重写、token 预算贪心填充,结构化注入 Text2SQL prompt。
动作校验(dry run)
POST /oag/actions/:name/validate 走 VALIDATE 模式,只校验不落盘,返回 validation_result 供前端确认。
动作执行
POST /oag/actions/:name/execute 透传 Foundry 动作执行契约(mode 可 VALIDATE_AND_EXECUTE / RUN / ASYNC),幂等键必填。
运行状态查询
GET /oag/actions/runs/:id 查询异步动作状态(pending / succeeded / failed)。
规模控制
对象数上限 20、每对象属性 8、指标 5、token 预算 2000,超限自动裁剪并标记 truncated。
降级链
OAG 失败 → 本地本体(BuildOntologyContext)→ RAG 关键词路径,演示不中断。
调整指南(怎么调整)
- 开关:在 config 配 foundry.oag.enabled=true + foundry.oag.base_url(缺省 http://localhost:18081),重启生效;未启用则动作代理端点 404。
- 列名映射:foundry.oag.column_map 把 Foundry 列名重写为 AIP 执行表列名(如 amount→sales_amount),不配会注入口径错列并 warn。
- 跨产品鉴权:AIP 用户 UUID 在 Foundry 用户库可能不存在(403),配置 foundry.oag.service_token 用 Foundry admin 服务账号,优先于用户 token。
- 数据源翻译:foundry.oag.data_source_map 把 AIP 数据源名翻译成 Foundry 数据源名,用于语义检索 filters。
- 规模参数:max_tokens / max_objects / max_props_per_object / max_metrics_per_object 可在 ServiceConfig 覆盖。
做得好的场景
OAG 在"口径已建模 + 对象可操作"时体验最完整:
- 口径不再靠猜:指标口径公式直接从 Foundry 注入,Text2SQL 按定义生成 SQL,比裸表元数据更准。
- 查询到操作闭环:对象可执行动作随检索返回(actions),前端能展示"对订单执行更新状态"等入口。
- 优雅降级:Foundry 挂了 / OAG 超时,自动退回本地本体与 RAG,演示与生产都不中断。
限制与不足
以下是明确的边界,使用前先知道:
- 依赖 Foundry:OAG 消费 Foundry 对象 / 指标 / 动作,Foundry 没建模或没起服务,对象路就是空的。
- 默认关闭:foundry.oag.enabled 默认 false,动作代理端点直接 404,需显式配置启用。
- 只做代理:AIP 对动作成败不做二次判定,HTTP 200 不等于业务成功(要看 validation_result.result==VALID)。
- 无对象级 UI:对象浏览 / 建模在 Foundry,AIP 侧只有注入与动作代理,没有对象工作台。
- 预算裁剪:对象数 / 属性 / 指标 / token 超限会被截断(truncated=true),注入信息可能不完整。
场景故事
故事 1
问"各产品销售额"——OAG 对象路把 Foundry 指标口径注入,SQL 一次生成对
场景:对象路查询
角色:业务分析师
耗时:约 3 分钟
- 背景
- Foundry 侧已经把"产品"建模成对象,销售额是对象的指标(口径公式 sales_amount 求和)。王姐在 AIP 的 /chat 输入"查询每个产品的销售额"——这次 OAG 对象路已启用(foundry.oag.enabled=true),她想看 NLQ 是否直接吃到了 Foundry 的指标口径,而不是靠裸表列名猜。
- 传统做法对比
- 以前 OAG 未启用时,NLQ 只能靠 RAG 元数据猜列,口径不一致时 SQL 容易写错(把 price 当金额);现在 OAG 把"产品对象 + 销售额指标公式 + 属性"结构化注入 prompt,模型按口径公式生成,一次写对。
- 角色
- 业务分析师(王姐,消费查询结果)。对象与指标由数据工程师在 Foundry 维护。
- 操作步骤
-
- 进入 /chat 智能查询
- 输入"查询每个产品的销售额"并回车
- 观察返回里的 rag_channels_used 是否含 "oag"
- 看 oag 元信息(object_count / metric_count / tokens_used)与生成 SQL
- 系统响应
- OAG 命中时,响应带 oag 对象路元信息与注入结果:
{
"intent": "data_query",
"confidence": 0.8,
"data_source_name": "aip_demo_warehouse",
"sql_query": "SELECT p.product_name, SUM(o.sales_amount) AS total_sales FROM orders o JOIN products p ON o.product_id = p.product_id GROUP BY p.product_name",
"rag_channels_used": ["oag"],
"ontology_hit": true,
"oag": {
"hit": true,
"object_count": 1,
"metric_count": 1,
"tokens_used": 180,
"truncated": false,
"total": 8
}
}
- 结果洞察
- rag_channels_used=["oag"] 说明这次上下文不是来自 RAG 元数据,而是 OAG 对象路:AIP 调 Foundry 语义检索(scopes 覆盖 metric/object/property,limit 20),把"产品对象 + 销售额指标(公式 SUM(sales_amount))+ 属性"结构化注入 Text2SQL prompt,ontology_hit=true。指标口径公式成了 prompt 的一部分,所以 SQL 一次就用了 sales_amount 且 JOIN 正确。对比 OAG 关闭时 rag_channels_used 会是 metadata/knowledge 等。
- 调整建议
- 对象 / 指标口径在 Foundry 维护,改口径后 AIP 下次查询自动生效;如果注入口径列与 AIP 实际执行列不一致,检查 foundry.oag.column_map 列名映射;响应里 oag.truncated=true 说明对象太多被裁剪,可调大 max_objects。
- 动手试一试
- 登录:admin / admin1。页面路径:/chat。输入内容:"查询每个产品的销售额"。预期结果:返回 rag_channels_used 含 "oag"、ontology_hit=true、oag 元信息显示 object_count > 0;SQL 使用 sales_amount 聚合。
- 限制提示
- OAG 命中依赖 Foundry 语义检索与对象级可见(object_level_visible);AIP 用户 UUID 在 Foundry 不存在时检索可能 403,需配 foundry.oag.service_token;Foundry 未启用 / 未起服务时 OAG 静默降级到本地本体 / RAG,响应里不会出现 oag 字段。
故事 2
对订单对象执行动作:先 validate 干跑校验,再 execute 真实执行
场景:对象操作
角色:业务分析师
耗时:约 5 分钟
- 背景
- Foundry 给"订单"对象定义了一个动作 update_order_status(更新订单状态)。王姐要在 AIP 里对一笔订单发起这个操作:先走 validate(干跑,不落盘)确认参数合法,再走 execute 真实执行。AIP 的动作代理端点负责把请求转发给 Foundry。
- 传统做法对比
- 以前改订单状态要登录业务系统手工改,或让 IT 调接口,改错无法回退;现在 AIP 里一步 validate 校验 + 一步 execute 执行,幂等键防重复,改了什么有 Foundry 审计。
- 角色
- 业务分析师(发起动作)+ IT 管理员(确保 OAG 已启用、服务账号已配置)。
- 操作步骤
-
- POST /api/v1/oag/actions/update_order_status/validate,body 带 object_type_id 与 params
- 查看 validation_result.result,确认是否 VALID
- POST /api/v1/oag/actions/update_order_status/execute,body 带 idempotency_key(幂等键)
- 查看执行响应(edits / latest_state / result)
- 系统响应
- validate(dry run)返回(validation_result 判定成败):
{
"code": 0,
"data": {
"mode": "VALIDATE",
"status": "completed",
"validation_result": { "result": "VALID", "issues": [] },
"edits": []
}
}
execute 返回(运行类模式,幂等键必填):{
"code": 0,
"data": {
"mode": "VALIDATE_AND_EXECUTE",
"status": "completed",
"validation_result": { "result": "VALID", "issues": [] },
"edits": [ { "object_type_id": 3, "object_id": "o-101", "property": "order_status", "new_value": "已发货" } ],
"latest_state": { "object_id": "o-101", "order_status": "已发货" }
}
}
- 结果洞察
- validate 走 Foundry 的 dry-run 校验(不落盘),validation_result.result==VALID 才算业务成功——注意 HTTP 200 不等于成功,成败要以这个字段为准(G3 契约)。execute 透传执行:edits 记录了属性改动(order_status→已发货)、latest_state 是执行后的对象状态。AIP 只做契约代理、零写路径逻辑,业务判定与幂等重放(REPLAYED)语义全部沿用 Foundry。
- 调整建议
- 运行类模式(RUN / ASYNC / VALIDATE_AND_EXECUTE)必须带 idempotency_key,否则 Foundry 返回 400;对敏感动作先 validate 确认参数,再 execute;高频重复操作靠幂等键去重,重放时 Foundry 返回 Status=REPLAYED。
- 动手试一试
- 前置:Foundry 已建模订单对象与 update_order_status 动作,foundry.oag.enabled=true。操作:POST /api/v1/oag/actions/update_order_status/validate 先校验,再 POST .../execute 带 {"idempotency_key":"demo-001", "object_type_id":3, "params":{...}}。预期结果:validate 返回 VALID;execute 返回 edits 与 latest_state;重复 execute 同一 idempotency_key 返回 REPLAYED。
- 限制提示
- OAG 未启用或 Foundry base_url 未配置时,动作代理端点返回明确 404("OAG 动作代理未启用"),不是路径写错;AIP 不做业务成败二次判定,务必看 validation_result;动作是否允许执行由 Foundry 侧 RBAC 决定(服务账号 token 优先)。
故事 3
异步动作轮询 + 未启用边界:GET runs/:id 看状态,没开 OAG 就 404
场景:状态查询与边界
角色:IT 管理员 + 业务分析师
耗时:约 10 分钟
- 背景
- 王姐用 ASYNC 模式发起了一个耗时的批量动作(重算所有订单的积分),返回了 operation_id(run_xxx)。她想轮询执行状态。同时,赵工在做环境检查时发现:另一台没配 Foundry 的测试机上,动作代理端点直接返回 404——他想搞清楚这是"没装"还是"配错了"。
- 传统做法对比
- 以前长任务只能等通知、看日志,状态不可查;现在 GET /oag/actions/runs/:id 随时查 pending / succeeded / failed。而"端点 404"在过去往往被当成 bug 排查半天,现在 AIP 把它定义为"能力未装配"的明确信号。
- 角色
- 业务分析师(轮询异步动作)+ IT 管理员(核对 OAG 启用配置)。
- 操作步骤
-
- 用 ASYNC 模式 POST /oag/actions/recalc_points/execute,记下 operation_id
- GET /oag/actions/runs/{operation_id} 轮询状态(pending → succeeded / failed)
- (边界验证)在未启用 OAG 的环境 POST /oag/actions/xxx/execute,观察 404
- 核对 config:foundry.oag.enabled、foundry.oag.base_url
- 系统响应
- 异步动作状态查询返回(对齐 Foundry okData 包装):
{
"code": 0,
"data": {
"operation_id": "run_42",
"action_name": "recalc_points",
"status": "succeeded",
"result": { "updated": 128, "skipped": 3 },
"mode": "ASYNC",
"created_at": "2026-08-10T09:00:00+08:00",
"updated_at": "2026-08-10T09:00:12+08:00"
}
}
未启用 OAG 时动作代理返回明确 404:{ "error": "OAG 动作代理未启用:请配置 foundry.oag.enabled=true 与 Foundry base_url" }
- 结果洞察
- ASYNC 模式下 execute 返回 operation_id,客户端用 GET runs/:id 轮询,status 在 pending → succeeded / failed 间变化,result 里带执行汇总(updated 128 / skipped 3)。404 语义被设计成"能力未装配"的明确信号——oagClient 为 nil 时端点返回 ierr 404,避免客户端把"没开 OAG"误判成"接口路径错"。赵工核对 config 后确认:这台测试机没配 foundry.oag.enabled=true,是预期行为。
- 调整建议
- 异步动作轮询建议加退避(如 2s / 5s / 10s),别高频打;线上环境把 foundry.oag.enabled=true + base_url + service_token 配齐后再开动作代理;把"404=未装配"写进运维排查清单,避免误报故障。
- 动手试一试
- 环境:已启用 OAG 的实例。操作:POST /oag/actions/recalc_points/execute(ASYNC)拿 operation_id → GET /oag/actions/runs/{id} 轮询。预期结果:状态从 pending 变 succeeded / failed,result 含执行汇总;在未启用 OAG 的环境重试,端点返回明确的"未启用"404。
- 限制提示
- 状态查询同样走服务账号 / 用户 token 鉴权,403 时先查 token 配置;operation_id 形如 run_<数字>,写错 ID 返回 404 或空结果;AIP 只透传 Foundry 的状态语义,不缓存不落本地。
常见问题
OAG 对象路和 RAG 是什么关系?
OAG 是 C1 批次引入的"对象级优先"上下文来源:启用且命中时,上下文直接来自 Foundry 对象(指标口径公式 + 对象 + 属性),rag_channels_used=["oag"];未启用 / 未命中 / 失败时依次降级本地本体(ontology)→ RAG 多路召回(metadata/knowledge/history/fewshot)。
为什么动作代理返回 404?
OAG 未启用(foundry.oag.enabled=false)或 Foundry base_url 未配置时 s.oagClient 为 nil,动作代理端点返回明确 404"OAG 动作代理未启用"。这是能力未装配的信号,不是路径写错。
HTTP 200 就代表动作成功吗?
不是。Foundry 契约 G3:HTTP 200 ≠ 业务成功,成败以响应体 validation_result.result==VALID 为准;幂等重放时返回 Status=REPLAYED(HTTP 200)。AIP 只做代理不判定,客户端必须看 validation_result。
动作执行必须带幂等键吗?
运行类模式(RUN / ASYNC / VALIDATE_AND_EXECUTE)强制 idempotency_key,缺失时 Foundry 返回 400;VALIDATE 模式(dry run)不落盘、无需幂等键。
AIP 用户能直接调 Foundry 动作吗?
可以,但 AIP 用户 UUID 在 Foundry 用户库可能不存在导致 403。建议配置 foundry.oag.service_token(Foundry admin 服务账号),服务账号优先于用户 token,解决跨产品用户库问题。
主题小结
一句话:OAG 对象路让 AIP"面向对象"——查数时把 Foundry 指标口径结构化注入(rag_channels_used=["oag"]),操作时经 validate / execute / runs 三个代理端点走通对象动作闭环。记住边界:默认关闭需显式启用、AIP 只做代理不判定成败、失败自动降级本地本体 / RAG、跨产品鉴权要配服务账号。