참고소스 수정본
This commit is contained in:
289
참고/ontocast-main/ontocast/agent/render_ontology.py
Normal file
289
참고/ontocast-main/ontocast/agent/render_ontology.py
Normal file
@@ -0,0 +1,289 @@
|
||||
"""Ontology triple rendering agent for OntoCast.
|
||||
|
||||
This module provides functionality for rendering RDF triples from ontologies into
|
||||
human-readable formats, making the ontological knowledge more accessible and
|
||||
understandable.
|
||||
The agent decides between generating bare Turtle for fresh ontologies and SPARQL operations for updates.
|
||||
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
from langchain_core.output_parsers import PydanticOutputParser
|
||||
from langchain_core.prompts import PromptTemplate
|
||||
|
||||
from ontocast.agent.common import call_llm_with_retry, render_suggestions_prompt
|
||||
from ontocast.onto.enum import FailureStage, Status, WorkflowNode
|
||||
from ontocast.onto.model import GraphUpdateRenderReport, OntologyRenderReport
|
||||
from ontocast.onto.rdfgraph import RDFGraph
|
||||
from ontocast.onto.unit_states import UnitOntologyState
|
||||
from ontocast.prompt.common import (
|
||||
ontology_template,
|
||||
output_instruction_sparql,
|
||||
output_instruction_ttl,
|
||||
text_template,
|
||||
)
|
||||
from ontocast.prompt.common import system_preamble_ontology as system_preamble
|
||||
from ontocast.prompt.render_ontology import (
|
||||
general_ontology_instruction,
|
||||
intro_instruction_fresh,
|
||||
intro_instruction_update,
|
||||
prefix_instruction,
|
||||
prefix_instruction_fresh,
|
||||
template_prompt,
|
||||
)
|
||||
from ontocast.tool.atomic import AtomicToolBox
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _extract_known_prefixes(state: UnitOntologyState) -> dict[str, str]:
|
||||
"""Extract ontology prefixes used to patch missing declarations in LLM TTL output."""
|
||||
current = state.current_ontology or state.ontology_snapshot
|
||||
known_prefixes: dict[str, str] = {}
|
||||
|
||||
if current and current.graph:
|
||||
for prefix, namespace_uri in current.graph.namespaces():
|
||||
if prefix: # Skip empty prefixes
|
||||
known_prefixes[prefix] = str(namespace_uri)
|
||||
|
||||
if current.prefix and current.namespace:
|
||||
known_prefixes[current.prefix] = current.namespace
|
||||
|
||||
return known_prefixes
|
||||
|
||||
|
||||
async def render_ontology(
|
||||
state: UnitOntologyState, tools: AtomicToolBox
|
||||
) -> UnitOntologyState:
|
||||
"""Structured hybrid ontology renderer with Turtle/SPARQL decision logic.
|
||||
|
||||
This function decides between generating bare Turtle for fresh ontologies
|
||||
and SPARQL operations for updates based on whether the ontology exists.
|
||||
|
||||
Args:
|
||||
state: The current unit ontology state
|
||||
tools: The toolbox containing necessary tools
|
||||
|
||||
Returns:
|
||||
UnitOntologyState: Updated state with rendered ontology
|
||||
"""
|
||||
|
||||
progress_info = state.get_content_unit_progress_string()
|
||||
logger.info(
|
||||
f"Ontology Renderer for {progress_info}: visit {state.node_visits[WorkflowNode.TEXT_TO_ONTOLOGY]}/{state.max_visits_per_node}"
|
||||
)
|
||||
current = state.current_ontology or state.ontology_snapshot
|
||||
# Guardrail for map/reduce flow: if a non-null snapshot exists, stay in update mode.
|
||||
has_seed_ontology = not state.ontology_snapshot.is_null()
|
||||
has_no_seed_ontology = current.is_null() and not has_seed_ontology
|
||||
|
||||
if has_no_seed_ontology:
|
||||
return await render_ontology_fresh(state, tools)
|
||||
else:
|
||||
return await render_ontology_update(state, tools)
|
||||
|
||||
|
||||
async def render_ontology_fresh(
|
||||
state: UnitOntologyState, tools: AtomicToolBox
|
||||
) -> UnitOntologyState:
|
||||
"""Render ontology triples into a human-readable format.
|
||||
|
||||
This function takes the triples from the current ontology and renders them
|
||||
into a more accessible format, making the ontological knowledge easier to
|
||||
understand.
|
||||
|
||||
Args:
|
||||
state: The current agent state containing the ontology to render.
|
||||
tools: The toolbox instance providing utility functions.
|
||||
|
||||
Returns:
|
||||
AgentState: Updated state with rendered triples.
|
||||
"""
|
||||
|
||||
parser = PydanticOutputParser(pydantic_object=OntologyRenderReport)
|
||||
logger.info("Rendering fresh ontology")
|
||||
intro_instruction = intro_instruction_fresh.format(
|
||||
current_domain=state.current_domain
|
||||
)
|
||||
output_instruction = output_instruction_ttl
|
||||
ontology_ttl = ""
|
||||
improvement_instruction_str = ""
|
||||
general_ontology_instruction_str = general_ontology_instruction.format(
|
||||
prefix_instruction=prefix_instruction_fresh
|
||||
)
|
||||
|
||||
text_chapter = text_template.format(text=state.content_unit.text)
|
||||
|
||||
external_evidence = state.external_evidence_text
|
||||
if external_evidence:
|
||||
state.mark_external_evidence_used(WorkflowNode.TEXT_TO_ONTOLOGY)
|
||||
|
||||
prompt = PromptTemplate(
|
||||
template=template_prompt,
|
||||
input_variables=[
|
||||
"preamble",
|
||||
"intro_instruction",
|
||||
"ontology_instruction",
|
||||
"output_instruction",
|
||||
"user_instruction",
|
||||
"improvement_instruction",
|
||||
"ontology_ttl",
|
||||
"text",
|
||||
"external_evidence",
|
||||
"format_instructions",
|
||||
],
|
||||
)
|
||||
|
||||
try:
|
||||
llm_tool = await tools.get_llm_tool(state.budget_tracker)
|
||||
render_report: OntologyRenderReport = await call_llm_with_retry(
|
||||
llm_tool=llm_tool,
|
||||
prompt=prompt,
|
||||
parser=parser,
|
||||
prompt_kwargs={
|
||||
"preamble": system_preamble,
|
||||
"intro_instruction": intro_instruction,
|
||||
"ontology_instruction": general_ontology_instruction_str,
|
||||
"output_instruction": output_instruction,
|
||||
"ontology_ttl": ontology_ttl,
|
||||
"user_instruction": state.ontology_user_instruction,
|
||||
"improvement_instruction": improvement_instruction_str,
|
||||
"text": text_chapter,
|
||||
"external_evidence": external_evidence,
|
||||
"format_instructions": parser.get_format_instructions(),
|
||||
},
|
||||
)
|
||||
state.set_external_evidence_request(
|
||||
WorkflowNode.TEXT_TO_ONTOLOGY, render_report.external_evidence_request
|
||||
)
|
||||
state.current_ontology = render_report.ontology
|
||||
state.current_ontology.graph.sanitize_prefixes_namespaces()
|
||||
|
||||
num_triples = len(state.current_ontology.graph)
|
||||
logger.info(f"New ontology created with {num_triples} triple(s).")
|
||||
|
||||
# Track triples in budget tracker (fresh ontology)
|
||||
state.budget_tracker.add_ontology_update(
|
||||
num_operations=1, num_triples=num_triples
|
||||
)
|
||||
|
||||
state.clear_failure()
|
||||
state.set_node_status(WorkflowNode.TEXT_TO_ONTOLOGY, Status.SUCCESS)
|
||||
return state
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to generate triples: {str(e)}")
|
||||
state.set_node_status(WorkflowNode.TEXT_TO_ONTOLOGY, Status.FAILED)
|
||||
state.set_failure(FailureStage.GENERATE_TTL_FOR_ONTOLOGY, str(e))
|
||||
return state
|
||||
|
||||
|
||||
async def render_ontology_update(
|
||||
state: UnitOntologyState, tools: AtomicToolBox
|
||||
) -> UnitOntologyState:
|
||||
"""Render ontology triples into a human-readable format.
|
||||
|
||||
This function takes the triples from the current ontology and renders them
|
||||
into a more accessible format, making the ontological knowledge easier to
|
||||
understand.
|
||||
|
||||
Args:
|
||||
state: The current unit ontology state containing the ontology to render.
|
||||
tools: The toolbox instance providing utility functions.
|
||||
|
||||
Returns:
|
||||
UnitOntologyState: Updated state with rendered triples.
|
||||
"""
|
||||
|
||||
parser = PydanticOutputParser(pydantic_object=GraphUpdateRenderReport)
|
||||
current = state.current_ontology or state.ontology_snapshot
|
||||
ontology_iri = current.iri
|
||||
ontology_desc = current.describe()
|
||||
intro_instruction = intro_instruction_update.format(
|
||||
ontology_iri=ontology_iri, ontology_desc=ontology_desc
|
||||
)
|
||||
ontology_chapter = ontology_template.format(
|
||||
ontology_ttl=current.graph.serialize(format="turtle")
|
||||
)
|
||||
output_instruction = output_instruction_sparql
|
||||
improvement_instruction_str = render_suggestions_prompt(
|
||||
state.suggestions, WorkflowNode.TEXT_TO_ONTOLOGY
|
||||
)
|
||||
|
||||
general_ontology_instruction_str = general_ontology_instruction.format(
|
||||
prefix_instruction=prefix_instruction.format(ontology_prefix=current.prefix),
|
||||
ontology_prefix=current.prefix,
|
||||
)
|
||||
text_chapter = text_template.format(text=state.content_unit.text)
|
||||
external_evidence = state.external_evidence_text
|
||||
if external_evidence:
|
||||
state.mark_external_evidence_used(WorkflowNode.TEXT_TO_ONTOLOGY)
|
||||
|
||||
prompt = PromptTemplate(
|
||||
template=template_prompt,
|
||||
input_variables=[
|
||||
"preamble",
|
||||
"intro_instruction",
|
||||
"ontology_instruction",
|
||||
"output_instruction",
|
||||
"user_instruction",
|
||||
"improvement_instruction",
|
||||
"ontology_ttl",
|
||||
"text",
|
||||
"external_evidence",
|
||||
"format_instructions",
|
||||
],
|
||||
)
|
||||
known_prefixes = _extract_known_prefixes(state)
|
||||
|
||||
try:
|
||||
llm_tool = await tools.get_llm_tool(state.budget_tracker)
|
||||
# Set known prefixes in context before parsing
|
||||
RDFGraph.set_known_prefixes(known_prefixes if known_prefixes else None)
|
||||
|
||||
render_report: GraphUpdateRenderReport = await call_llm_with_retry(
|
||||
llm_tool=llm_tool,
|
||||
prompt=prompt,
|
||||
parser=parser,
|
||||
prompt_kwargs={
|
||||
"preamble": system_preamble,
|
||||
"intro_instruction": intro_instruction,
|
||||
"ontology_instruction": general_ontology_instruction_str,
|
||||
"output_instruction": output_instruction,
|
||||
"improvement_instruction": improvement_instruction_str,
|
||||
"ontology_ttl": ontology_chapter,
|
||||
"user_instruction": state.ontology_user_instruction,
|
||||
"text": text_chapter,
|
||||
"external_evidence": external_evidence,
|
||||
"format_instructions": parser.get_format_instructions(),
|
||||
},
|
||||
)
|
||||
state.set_external_evidence_request(
|
||||
WorkflowNode.TEXT_TO_ONTOLOGY, render_report.external_evidence_request
|
||||
)
|
||||
graph_update = render_report.graph_update
|
||||
state.ontology_updates.append(graph_update)
|
||||
state.update_ontology()
|
||||
|
||||
num_operations, num_triples = graph_update.count_total_triples()
|
||||
logger.info(
|
||||
f"Ontology update has {num_operations} operation(s) "
|
||||
f"with {num_triples} total triple(s)."
|
||||
)
|
||||
|
||||
# Track triples in budget tracker
|
||||
state.budget_tracker.add_ontology_update(num_operations, num_triples)
|
||||
|
||||
state.clear_failure()
|
||||
state.set_node_status(WorkflowNode.TEXT_TO_ONTOLOGY, Status.SUCCESS)
|
||||
return state
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to generate ontology update: {str(e)}")
|
||||
state.set_node_status(WorkflowNode.TEXT_TO_ONTOLOGY, Status.FAILED)
|
||||
state.set_failure(FailureStage.GENERATE_SPARQL_UPDATE_FOR_ONTOLOGY, str(e))
|
||||
return state
|
||||
finally:
|
||||
# Clear the context after parsing
|
||||
RDFGraph.set_known_prefixes(None)
|
||||
Reference in New Issue
Block a user