125 lines
4.0 KiB
Python
125 lines
4.0 KiB
Python
"""
|
|
Oracle agent node — the high-IQ architecture and debugging advisor.
|
|
|
|
Loki equivalent: assets/agents/oracle/config.yaml + tools.sh
|
|
|
|
In Loki, the oracle is a READ-ONLY advisor spawned for:
|
|
- Architecture decisions and multi-system tradeoffs
|
|
- Complex debugging (after 2+ failed fix attempts)
|
|
- Code/design review
|
|
- Risk assessment
|
|
|
|
It uses temperature 0.2 (slightly higher than explore/coder for more creative
|
|
reasoning) and ends with "ORACLE_COMPLETE".
|
|
|
|
In LangGraph, oracle is a node that receives the full message history, reasons
|
|
about the problem, and writes structured advice back. It has read-only tools
|
|
only — it never modifies files.
|
|
|
|
Key Loki→LangGraph mapping:
|
|
- Loki oracle triggers (the "MUST spawn oracle when..." rules in sisyphus)
|
|
become routing conditions in the supervisor node.
|
|
- Oracle's structured output format (Analysis/Recommendation/Reasoning/Risks)
|
|
is enforced via the system prompt, same as in Loki.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from langchain_core.messages import SystemMessage
|
|
from langchain_openai import ChatOpenAI
|
|
|
|
from sisyphus_langchain.state import SisyphusState
|
|
from sisyphus_langchain.tools.filesystem import (
|
|
list_directory,
|
|
read_file,
|
|
search_content,
|
|
search_files,
|
|
)
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# System prompt — faithfully mirrors oracle/config.yaml
|
|
# ---------------------------------------------------------------------------
|
|
ORACLE_SYSTEM_PROMPT = """\
|
|
You are Oracle — a senior architect and debugger consulted for complex decisions.
|
|
|
|
## Your Role
|
|
|
|
You are READ-ONLY. You analyze, advise, and recommend. You do NOT implement.
|
|
|
|
## When You're Consulted
|
|
|
|
1. **Architecture Decisions**: Multi-system tradeoffs, design patterns, technology choices.
|
|
2. **Complex Debugging**: After 2+ failed fix attempts, deep analysis needed.
|
|
3. **Code Review**: Evaluating proposed designs or implementations.
|
|
4. **Risk Assessment**: Security, performance, or reliability concerns.
|
|
|
|
## Your Process
|
|
|
|
1. **Understand**: Read relevant code, understand the full context.
|
|
2. **Analyze**: Consider multiple angles and tradeoffs.
|
|
3. **Recommend**: Provide clear, actionable advice.
|
|
4. **Justify**: Explain your reasoning.
|
|
|
|
## Output Format
|
|
|
|
Structure your response as:
|
|
|
|
## Analysis
|
|
[Your understanding of the situation]
|
|
|
|
## Recommendation
|
|
[Clear, specific advice]
|
|
|
|
## Reasoning
|
|
[Why this is the right approach]
|
|
|
|
## Risks/Considerations
|
|
[What to watch out for]
|
|
|
|
## Rules
|
|
|
|
1. Never modify files — you advise, others implement.
|
|
2. Be thorough — read all relevant context before advising.
|
|
3. Be specific — general advice isn't helpful.
|
|
4. Consider tradeoffs — there are rarely perfect solutions.
|
|
5. Stay focused — answer the specific question asked.
|
|
"""
|
|
|
|
# Read-only tools — same set as explore (oracle never writes)
|
|
ORACLE_TOOLS = [read_file, search_content, search_files, list_directory]
|
|
|
|
|
|
def create_oracle_node(model_name: str = "gpt-4o", temperature: float = 0.2):
|
|
"""
|
|
Factory that returns an oracle node function.
|
|
|
|
Oracle uses a more expensive model than explore because it needs deeper
|
|
reasoning. In Loki, the model is inherited from the global config unless
|
|
overridden in oracle/config.yaml.
|
|
|
|
Args:
|
|
model_name: Model identifier (use a strong reasoning model).
|
|
temperature: LLM temperature (Loki oracle uses 0.2).
|
|
"""
|
|
llm = ChatOpenAI(model=model_name, temperature=temperature).bind_tools(ORACLE_TOOLS)
|
|
|
|
def oracle_node(state: SisyphusState) -> dict:
|
|
"""
|
|
LangGraph node: run the oracle agent.
|
|
|
|
Reads conversation history, applies the oracle system prompt,
|
|
invokes the LLM, and returns structured advice.
|
|
"""
|
|
response = llm.invoke(
|
|
[SystemMessage(content=ORACLE_SYSTEM_PROMPT)] + state["messages"]
|
|
)
|
|
return {
|
|
"messages": [response],
|
|
"agent_outputs": {
|
|
**state.get("agent_outputs", {}),
|
|
"oracle": response.content,
|
|
},
|
|
}
|
|
|
|
return oracle_node
|