PlanOpticon

planopticon / video_processor / agent / agent_loop.py
Blame History Raw 160 lines
1
"""Planning agent loop for synthesizing knowledge into artifacts."""
2
3
import logging
4
from pathlib import Path
5
from typing import List
6
7
from video_processor.agent.kb_context import KBContext
8
from video_processor.agent.skills.base import (
9
AgentContext,
10
Artifact,
11
get_skill,
12
list_skills,
13
)
14
15
logger = logging.getLogger(__name__)
16
17
18
class PlanningAgent:
19
"""AI agent that synthesizes knowledge into planning artifacts."""
20
21
def __init__(self, context: AgentContext):
22
self.context = context
23
24
@classmethod
25
def from_kb_paths(cls, kb_paths: List[Path], provider_manager=None) -> "PlanningAgent":
26
"""Create an agent from knowledge base paths."""
27
kb = KBContext()
28
for path in kb_paths:
29
kb.add_source(path)
30
kb.load(provider_manager=provider_manager)
31
32
context = AgentContext(
33
knowledge_graph=kb.knowledge_graph,
34
query_engine=kb.query_engine,
35
provider_manager=provider_manager,
36
)
37
return cls(context)
38
39
def execute(self, request: str) -> List[Artifact]:
40
"""Execute a user request by selecting and running appropriate skills."""
41
# Step 1: Build context summary for LLM
42
kb_summary = ""
43
if self.context.query_engine:
44
stats = self.context.query_engine.stats()
45
kb_summary = stats.to_text()
46
47
available_skills = list_skills()
48
skill_descriptions = "\n".join(f"- {s.name}: {s.description}" for s in available_skills)
49
50
# Step 2: Ask LLM to select skills
51
plan_prompt = (
52
"You are a planning agent. Given a user request and available skills, "
53
"select which skills to execute and in what order.\n\n"
54
f"Knowledge base:\n{kb_summary}\n\n"
55
f"Available skills:\n{skill_descriptions}\n\n"
56
f"User request: {request}\n\n"
57
"Return a JSON array of skill names to execute in order:\n"
58
'[{"skill": "skill_name", "params": {}}]\n'
59
"Return ONLY the JSON array."
60
)
61
62
if not self.context.provider_manager:
63
# No LLM -- try to match skills by keyword
64
return self._keyword_match_execute(request)
65
66
raw = self.context.provider_manager.chat(
67
[{"role": "user", "content": plan_prompt}],
68
max_tokens=512,
69
temperature=0.1,
70
)
71
72
from video_processor.utils.json_parsing import parse_json_from_response
73
74
plan = parse_json_from_response(raw)
75
76
artifacts = []
77
if isinstance(plan, list):
78
for step in plan:
79
if isinstance(step, dict) and "skill" in step:
80
skill = get_skill(step["skill"])
81
if skill and skill.can_execute(self.context):
82
params = step.get("params", {})
83
artifact = skill.execute(self.context, **params)
84
artifacts.append(artifact)
85
self.context.artifacts.append(artifact)
86
87
return artifacts
88
89
def _keyword_match_execute(self, request: str) -> List[Artifact]:
90
"""Fallback: match skills by keywords in the request."""
91
request_lower = request.lower()
92
artifacts = []
93
for skill in list_skills():
94
# Simple keyword matching
95
skill_words = skill.name.replace("_", " ").split()
96
if any(word in request_lower for word in skill_words):
97
if skill.can_execute(self.context):
98
artifact = skill.execute(self.context)
99
artifacts.append(artifact)
100
self.context.artifacts.append(artifact)
101
return artifacts
102
103
def chat(self, message: str) -> str:
104
"""Interactive chat -- accumulate context and answer questions."""
105
self.context.conversation_history.append({"role": "user", "content": message})
106
107
if not self.context.provider_manager:
108
return "Agent requires a configured LLM provider for chat mode."
109
110
# Build system context
111
kb_summary = ""
112
if self.context.query_engine:
113
stats = self.context.query_engine.stats()
114
kb_summary = f"\n\nKnowledge base:\n{stats.to_text()}"
115
116
artifacts_summary = ""
117
if self.context.artifacts:
118
artifacts_summary = "\n\nGenerated artifacts:\n" + "\n".join(
119
f"- {a.name} ({a.artifact_type})" for a in self.context.artifacts
120
)
121
122
system_msg = (
123
"You are PlanOpticon, an AI planning companion built into the PlanOpticon CLI. "
124
"PlanOpticon is a video analysis and knowledge extraction tool that processes "
125
"recordings into structured knowledge graphs.\n\n"
126
"You are running inside the interactive companion REPL. The user can use these "
127
"built-in commands (suggest them when relevant):\n"
128
" /status - Show workspace status (loaded KG, videos, docs)\n"
129
" /entities [--type T] - List knowledge graph entities\n"
130
" /search TERM - Search entities by name\n"
131
" /neighbors ENTITY - Show entity relationships\n"
132
" /export FORMAT - Export KG (markdown, obsidian, notion, csv)\n"
133
" /analyze PATH - Analyze a video or document\n"
134
" /ingest PATH - Ingest a file into the knowledge graph\n"
135
" /auth SERVICE - Authenticate with a service "
136
"(zoom, google, microsoft, notion, dropbox, github)\n"
137
" /provider [NAME] - List or switch LLM provider\n"
138
" /model [NAME] - Show or switch chat model\n"
139
" /plan - Generate a project plan\n"
140
" /prd - Generate a PRD\n"
141
" /tasks - Generate a task breakdown\n\n"
142
"PlanOpticon CLI commands the user can run outside the REPL:\n"
143
" planopticon auth zoom|google|microsoft - Authenticate with cloud services\n"
144
" planopticon recordings zoom-list|teams-list|meet-list - List cloud recordings\n"
145
" planopticon analyze -i VIDEO - Analyze a video file\n"
146
" planopticon query - Query the knowledge graph\n"
147
" planopticon export FORMAT PATH - Export knowledge graph\n\n"
148
f"{kb_summary}{artifacts_summary}\n\n"
149
"Help the user with their planning tasks. When they ask about capabilities, "
150
"refer them to the appropriate built-in commands. Ask clarifying questions "
151
"to gather requirements. When ready, suggest using specific skills or commands "
152
"to generate artifacts."
153
)
154
155
messages = [{"role": "system", "content": system_msg}] + self.context.conversation_history
156
157
response = self.context.provider_manager.chat(messages, max_tokens=2048, temperature=0.5)
158
self.context.conversation_history.append({"role": "assistant", "content": response})
159
return response
160

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button