| 22 | |
| 23 | |
| 24 | class ContextGraphSkill: |
| 25 | |
| 26 | def ingest(self, documents: list[str]) -> None: |
| 27 | """ |
| 28 | Orchestration entry point for ingesting documents into the context graph. |
| 29 | |
| 30 | The agent (Copilot) MUST: |
| 31 | 1. Read ingestion.md to understand entity/relation extraction rules. |
| 32 | 2. Read ontology.md to apply type normalization. |
| 33 | 3. For each document, produce a JSON with entities + relations. |
| 34 | 4. For each entity: |
| 35 | - ontology_store.add_type(entity["type"]) |
| 36 | - node_id = graph_store.add_node(entity["name"], entity["type"]) |
| 37 | - index_store.add_entity(entity["name"], node_id) |
| 38 | 5. For each relation (if confidence >= MIN_CONFIDENCE): |
| 39 | - ontology_store.add_relation(relation["type"]) |
| 40 | - source_id = graph_store.find_node_by_name(relation["source"]) |
| 41 | - target_id = graph_store.find_node_by_name(relation["target"]) |
| 42 | - graph_store.add_edge(source_id, target_id, relation["type"], relation["confidence"]) |
| 43 | |
| 44 | This method does NOT call any LLM. It documents the agent contract only. |
| 45 | """ |
| 46 | raise NotImplementedError( |
| 47 | "ingest() must be driven by the Copilot agent following ingestion.md. " |
| 48 | "Call the tool methods directly after LLM extraction." |
| 49 | ) |
| 50 | |
| 51 | def query(self, query: str) -> dict: |
| 52 | """ |
| 53 | Orchestration entry point for retrieving a subgraph for a query. |
| 54 | |
| 55 | The agent (Copilot) MUST: |
| 56 | 1. Read retrieval.md to understand the retrieval strategy. |
| 57 | 2. Call index_store.search(query) to get seed node_ids. |
| 58 | 3. Call retrieval_engine.retrieve(seed_ids, depth=MAX_GRAPH_DEPTH) to expand. |
| 59 | 4. Call graph_store.get_subgraph(node_ids) to build the result. |
| 60 | 5. Return the subgraph dict. |
| 61 | |
| 62 | This method does NOT call any LLM. It documents the agent contract only. |
| 63 | Returns an empty subgraph if called directly. |
| 64 | """ |
| 65 | seed_ids = index_store.search(query) |
| 66 | if not seed_ids: |
| 67 | return {"nodes": {}, "edges": []} |
| 68 | |
| 69 | node_ids = retrieval_engine.retrieve( |
| 70 | seed_ids, |
| 71 | depth=config.MAX_GRAPH_DEPTH, |
| 72 | min_confidence=config.MIN_CONFIDENCE, |
| 73 | max_nodes=config.MAX_NODES, |
| 74 | ) |
| 75 | return graph_store.get_subgraph(node_ids) |
| 76 | |
| 77 | # ------------------------------------------------------------------ |
| 78 | # Convenience wrappers — agents may call these directly |
| 79 | # ------------------------------------------------------------------ |
| 80 | |
| 81 | def add_node(self, name: str, node_type: str) -> str: |