feat(query): generalize hybrid business wiki routing
This commit is contained in:
@@ -76,24 +76,33 @@ If it is a business facts query:
|
||||
4. If the template or guarded SQL returns an answer, answer from that JSON result as structured business facts. Do not cite wiki pages unless you also used wiki pages for business context. Mention stale sync warnings when `is_stale` is true.
|
||||
5. If the CLI/database is unavailable or the question is not a business facts query, continue with the normal wiki retrieval protocol below.
|
||||
|
||||
### Step 1b: Hybrid Business + Wiki Route (subagent split)
|
||||
### Step 1b: Hybrid Business + Wiki Route (portable two-track)
|
||||
|
||||
Do not use hybrid mode when filtered mode is active. In filtered mode, skip business facts entirely and continue with ordinary filtered wiki-query retrieval.
|
||||
|
||||
Use hybrid mode when the user asks for an entity overview, situation, background, profile, current status, or "大体情况" where both wiki context and business facts would materially improve the answer. Common examples: customer overview with recent orders, customer relationship status, product situation with order signals, salesperson activity with related customers/projects, or "告诉我一下某某客户的大体情况".
|
||||
Use hybrid mode when the user's question needs both structured business facts and wiki context to answer well. This is not customer-specific. Treat these as common hybrid categories:
|
||||
|
||||
For a hybrid query, spawn two subagents when the platform supports subagents:
|
||||
- customer/account health — orders, sales, trends, main products, relationship history, risks, and open opportunities
|
||||
- product/material situation — quantity, sales, customer spread, recent orders, specs, quality records, competitors, and application context
|
||||
- salesperson/team activity — sales amount, order count, active customers/products, related projects, and follow-up context
|
||||
- project progress with commercial signal — wiki project status plus related orders, products, customers, or delivery plans
|
||||
- delivery risk — planned delivery dates/quantities, order status, customer urgency, project milestones, and historical delay signals
|
||||
- anomaly explanation — business trend/ranking changes plus wiki evidence such as emails, complaints, quality issues, project pauses, or relationship changes
|
||||
- TOP ranking context — ranked customers/products/salespeople/projects plus wiki explanation of why they matter and what needs attention
|
||||
- quality or complaint impact — wiki quality/complaint records plus affected customers, products, orders, quantities, sales, or trends
|
||||
|
||||
1. **ordinary wiki-query subagent** — run the normal wiki-query retrieval path only. Do not use business facts. Return concise narrative context with wiki citations: customer/project background, relationships, email-derived signals, risks, open questions, and pages consulted.
|
||||
2. **business facts query subagent** — run only the business facts route. Load `obsidian-wiki business schema --json`, prefer templates such as `customer_summary`, `customer_order_list`, `breakdown_by_product` with `--customer`, `order_trend`/`sales_trend` with customer filters, or guarded `business sql` when no template fits. Return structured metrics, rows, `last_sync`, and `is_stale`.
|
||||
For a hybrid query, run two independent tracks; parallel subagents are optional. Use them when the host platform supports reliable subagents; otherwise run the same tracks sequentially in this agent and then merge the results.
|
||||
|
||||
The main agent must wait for both tracks, then fuse the two results into one answer:
|
||||
1. **ordinary wiki-query track** — run the normal wiki-query retrieval path only. Do not use business facts. Return concise narrative context with wiki citations: customer/project/product background, relationships, email-derived signals, quality or complaint history, risks, open questions, and pages consulted.
|
||||
2. **business facts query track** — run only the business facts route. Load `obsidian-wiki business schema --json`, prefer templates such as `customer_summary`, `product_summary`, `staff_summary`, `customer_order_list`, `product_order_list`, `staff_order_list`, `top_customers`, `top_products`, `top_staff`, `top_projects`, `delivery_plan_list`, `order_trend`, `sales_trend`, `breakdown_by_product`, `breakdown_by_customer`, `breakdown_by_staff`, or guarded `business sql` when no template fits. Return structured metrics, rows, `last_sync`, and `is_stale`.
|
||||
|
||||
- Lead with the business snapshot when available: recent order count, sales amount, main products, latest orders, trend, and data freshness.
|
||||
- Then add wiki context: who the customer is, related projects/products, important relationship history, risks, and citations.
|
||||
The answering agent must wait for both tracks when they run in parallel, or finish both sequential tracks before answering. Then fuse the two results into one answer:
|
||||
|
||||
- Lead with the business snapshot when available: relevant counts, sales amount, quantities, rankings, latest orders, delivery signals, trend, and data freshness.
|
||||
- Then add wiki context: entity background, related projects/products/customers, important relationship history, quality or complaint signals, risks, and citations.
|
||||
- Keep business numbers separate from narrative claims; cite wiki pages only for wiki-derived statements.
|
||||
- If one track fails or has no data, still answer from the other track and state that gap.
|
||||
- If subagents are not available, run the same two tracks sequentially and mention only material limitations, not internal tooling details.
|
||||
- Do not mention whether the tracks ran as subagents or sequentially; mention only material data limitations.
|
||||
|
||||
### Step 2: Index Pass (cheap)
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ obsidian-wiki setup --project . # also drop project-local skills + AGENTS.md int
|
||||
obsidian-wiki setup --copy # copy skill files instead of symlinking
|
||||
```
|
||||
|
||||
For operational questions such as "2026 年 6 月接了多少单", ask your agent with `wiki-query`. The `wiki-query` skill decides whether the question needs structured business facts, then uses the business template catalog or guarded read-only SQL before falling back to normal wiki retrieval. For hybrid questions such as "某客户的大体情况", it splits the work into a normal wiki context track and a business facts track, then fuses the narrative context with recent order/customer metrics.
|
||||
For operational questions such as "2026 年 6 月接了多少单", ask your agent with `wiki-query`. The `wiki-query` skill decides whether the question needs structured business facts, then uses the business template catalog or guarded read-only SQL before falling back to normal wiki retrieval. For hybrid questions about customers, products, salespeople, projects, delivery risk, anomalies, rankings, or quality impact, it runs a normal wiki context track and a business facts track, then fuses narrative context with structured metrics.
|
||||
|
||||
When working from a cloned repo, `scripts/obsidian-wiki-ops.py` is the single repo-local operations entrypoint. It wraps common `obsidian-wiki` commands, Kingdee business sync/templates, the business sync timer, daily update, and the managed MCP service:
|
||||
|
||||
@@ -327,7 +327,7 @@ Staging is for upload/intake only. The normal ingest flow should still classify
|
||||
|
||||
### Optional: Business Fact Queries
|
||||
|
||||
Operational counts and amounts belong in a structured fact database, not in markdown pages. The `business` CLI stores Kingdee sales orders in `<vault>/_meta/business-facts.sqlite`. Normal users should ask through `wiki-query`; the skill decides when to use structured business facts, prefers templates, and only falls back to guarded read-only SQL when no template fits. For customer/product/person overview questions, `wiki-query` runs a hybrid path: one track gathers wiki context and citations, another gathers read-only business metrics, and the final answer keeps the two sources clearly separated.
|
||||
Operational counts and amounts belong in a structured fact database, not in markdown pages. The `business` CLI stores Kingdee sales orders in `<vault>/_meta/business-facts.sqlite`. Normal users should ask through `wiki-query`; the skill decides when to use structured business facts, prefers templates, and only falls back to guarded read-only SQL when no template fits. For mixed business-and-context questions, `wiki-query` runs a hybrid path: one track gathers wiki context and citations, another gathers read-only business metrics, and the final answer keeps the two sources clearly separated.
|
||||
|
||||
```bash
|
||||
# Sync from an exported or API-fetched Kingdee JSON payload.
|
||||
@@ -502,7 +502,7 @@ This adds read-oriented OpenCode-backed query tools to the same `wiki-file-asset
|
||||
|
||||
The managed service config sets query to `OBSIDIAN_MCP_QUERY_OPENCODE_MODEL=openai/gpt-5.5` and `OBSIDIAN_MCP_QUERY_OPENCODE_VARIANT=minimal` by default, prioritizing response speed over maximum reasoning depth. Tune the server env file if a different balance is needed.
|
||||
|
||||
`public_only` defaults to `false`, so the answer may use the same full vault visibility that a local operator sees. Set `public_only=true` when the remote answer must exclude `visibility/internal` and `visibility/pii` pages. Job state and logs live under `.runtime/mcp-query/jobs/`, outside the vault. A normal `wiki-query` log entry is allowed. The generated prompt forbids ingest/update/rebuild workflows, writes, sync/config/timer operations, and arbitrary shell execution; it only allows the `obsidian-wiki business schema/template/sql` read-only commands needed by the wiki-query business facts route. When `public_only=true`, business facts remain disabled.
|
||||
`public_only` defaults to `false`, so the answer may use the same full vault visibility that a local operator sees. Set `public_only=true` when the remote answer must exclude `visibility/internal` and `visibility/pii` pages. Job state and logs live under `.runtime/mcp-query/jobs/`, outside the vault. A normal `wiki-query` log entry is allowed. The generated prompt forbids ingest/update/rebuild workflows, writes, sync/config/timer operations, and arbitrary shell execution; it only allows the `obsidian-wiki business schema/template/sql` read-only commands needed by the wiki-query business facts route. Hybrid business + wiki questions may run a wiki context track and a business facts track, using parallel subagents only when the host supports them and otherwise running sequentially. When `public_only=true`, business facts remain disabled.
|
||||
|
||||
Rotate the token later with:
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ Order upsert key: `order_id`, falling back to `order_no` when no explicit ID is
|
||||
|
||||
## Query Flow And Counting Basis
|
||||
|
||||
`wiki-query` decides semantically whether a user question needs business facts. When it does, it loads `obsidian-wiki business schema --json`, chooses a template if one fits, and only then uses guarded SQL for gaps. For hybrid overview questions such as "某客户的大体情况", it runs two tracks: ordinary wiki retrieval for context/citations and business facts retrieval for customer/order metrics, then fuses the results in one answer.
|
||||
`wiki-query` decides semantically whether a user question needs business facts. When it does, it loads `obsidian-wiki business schema --json`, chooses a template if one fits, and only then uses guarded SQL for gaps. For hybrid questions about customers, products, salespeople, projects, delivery risk, anomalies, rankings, or quality impact, it runs two tracks: ordinary wiki retrieval for context/citations and business facts retrieval for structured metrics, then fuses the results in one answer.
|
||||
|
||||
Template families include:
|
||||
|
||||
|
||||
@@ -266,7 +266,7 @@ class QueryManager:
|
||||
"Do not run arbitrary shell commands or commands outside this prompt's allowlist.",
|
||||
"Do not write files, run source-file ingest workflows, wiki-ingest, wiki-update, wiki-rebuild, Kingdee sync/config/timer commands, or any other mutating operation.",
|
||||
"For business facts questions only, the wiki-query skill may run these read-only business facts commands: `obsidian-wiki business schema --json`, `obsidian-wiki business template ... --vault \"$OBSIDIAN_VAULT_PATH\" ... --json`, and `obsidian-wiki business sql --vault \"$OBSIDIAN_VAULT_PATH\" \"<SELECT or WITH SELECT>\" --json`.",
|
||||
"For hybrid business + wiki questions such as entity/customer overviews, the wiki-query skill may spawn read-only subagents: one ordinary wiki-query track and one business facts query track, then fuse the two results.",
|
||||
"For hybrid business + wiki questions involving customer, product, salesperson, project, delivery, anomaly, ranking, or quality context, the wiki-query skill may run two read-only tracks: one ordinary wiki context track and one business facts query track. It may use parallel subagents when the host supports them; otherwise it must run those tracks sequentially, then fuse the two results.",
|
||||
"Business SQL must follow the schema guardrails: only SELECT/WITH SELECT against documented structured tables, never raw_json, raw_snapshots, writes, DDL, PRAGMA, or sync/config operations.",
|
||||
"If Visibility is public only, do not use business facts; continue with filtered wiki retrieval.",
|
||||
"Return the final synthesized answer with wiki citations when available.",
|
||||
|
||||
@@ -121,7 +121,11 @@ class McpQueryTests(unittest.TestCase):
|
||||
self.assertIn("obsidian-wiki business sql", prompt)
|
||||
self.assertIn("read-only business facts", prompt)
|
||||
self.assertIn("hybrid business + wiki questions", prompt)
|
||||
self.assertIn("spawn read-only subagents", prompt)
|
||||
self.assertIn("two read-only tracks", prompt)
|
||||
self.assertIn("parallel subagents when the host supports them", prompt)
|
||||
self.assertIn("run those tracks sequentially", prompt)
|
||||
self.assertIn("customer, product, salesperson, project, delivery, anomaly, ranking, or quality", prompt)
|
||||
self.assertNotIn("spawn read-only subagents", prompt)
|
||||
self.assertIn("Do not run arbitrary shell commands", prompt)
|
||||
self.assertIn("If Visibility is public only, do not use business facts", prompt)
|
||||
self.assertNotIn("shell commands, file writes", prompt)
|
||||
|
||||
@@ -7,7 +7,18 @@ class WikiSkillInstructionTests(unittest.TestCase):
|
||||
text = Path(".skills/wiki-query/SKILL.md").read_text(encoding="utf-8")
|
||||
|
||||
self.assertIn("Hybrid Business + Wiki Route", text)
|
||||
self.assertIn("spawn two subagents", text)
|
||||
self.assertNotIn("spawn two subagents", text)
|
||||
self.assertIn("two independent tracks", text)
|
||||
self.assertIn("parallel subagents are optional", text)
|
||||
self.assertIn("run the same tracks sequentially", text)
|
||||
self.assertIn("customer/account health", text)
|
||||
self.assertIn("product/material situation", text)
|
||||
self.assertIn("salesperson/team activity", text)
|
||||
self.assertIn("project progress with commercial signal", text)
|
||||
self.assertIn("delivery risk", text)
|
||||
self.assertIn("anomaly explanation", text)
|
||||
self.assertIn("TOP ranking context", text)
|
||||
self.assertIn("quality or complaint impact", text)
|
||||
self.assertIn("ordinary wiki-query", text)
|
||||
self.assertIn("business facts query", text)
|
||||
self.assertIn("fuse the two results", text)
|
||||
|
||||
Reference in New Issue
Block a user