universal-mcp-agents 0.1.12__py3-none-any.whl → 0.1.14__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of universal-mcp-agents might be problematic. Click here for more details.
- universal_mcp/agents/__init__.py +1 -1
- universal_mcp/agents/base.py +2 -0
- universal_mcp/agents/bigtool/__init__.py +1 -1
- universal_mcp/agents/bigtool/agent.py +2 -2
- universal_mcp/agents/bigtool/graph.py +65 -31
- universal_mcp/agents/bigtool/prompts.py +2 -2
- universal_mcp/agents/bigtool/tools.py +18 -4
- universal_mcp/agents/builder/__main__.py +105 -30
- universal_mcp/agents/builder/builder.py +149 -160
- universal_mcp/agents/builder/helper.py +73 -0
- universal_mcp/agents/builder/prompts.py +33 -152
- universal_mcp/agents/builder/state.py +1 -1
- universal_mcp/agents/cli.py +2 -2
- universal_mcp/agents/codeact/agent.py +1 -1
- universal_mcp/agents/codeact/sandbox.py +1 -5
- universal_mcp/agents/codeact0/agent.py +5 -4
- universal_mcp/agents/codeact0/langgraph_agent.py +17 -0
- universal_mcp/agents/codeact0/llm_tool.py +1 -1
- universal_mcp/agents/codeact0/prompts.py +34 -23
- universal_mcp/agents/codeact0/usecases/11-github.yaml +6 -5
- universal_mcp/agents/codeact0/utils.py +42 -63
- universal_mcp/agents/shared/__main__.py +43 -0
- universal_mcp/agents/shared/prompts.py +50 -99
- universal_mcp/agents/shared/tool_node.py +149 -203
- universal_mcp/agents/utils.py +65 -0
- universal_mcp/applications/ui/app.py +2 -2
- {universal_mcp_agents-0.1.12.dist-info → universal_mcp_agents-0.1.14.dist-info}/METADATA +1 -1
- {universal_mcp_agents-0.1.12.dist-info → universal_mcp_agents-0.1.14.dist-info}/RECORD +29 -28
- universal_mcp/agents/codeact0/langgraph_graph.py +0 -17
- universal_mcp/agents/codeact0/legacy_codeact.py +0 -104
- {universal_mcp_agents-0.1.12.dist-info → universal_mcp_agents-0.1.14.dist-info}/WHEEL +0 -0
|
@@ -1,42 +1,26 @@
|
|
|
1
1
|
import json
|
|
2
|
-
from collections import defaultdict
|
|
3
2
|
|
|
4
|
-
from langchain_core.
|
|
5
|
-
from langchain_core.messages import AIMessage, HumanMessage
|
|
3
|
+
from langchain_core.messages import HumanMessage
|
|
6
4
|
from langgraph.checkpoint.base import BaseCheckpointSaver
|
|
7
5
|
from langgraph.graph import END, START, StateGraph
|
|
8
6
|
from loguru import logger
|
|
9
7
|
from universal_mcp.tools.registry import ToolRegistry
|
|
10
8
|
from universal_mcp.types import ToolConfig
|
|
9
|
+
from langgraph.types import Command
|
|
11
10
|
|
|
12
11
|
from universal_mcp.agents.base import BaseAgent
|
|
13
12
|
from universal_mcp.agents.builder.prompts import (
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
TASK_SYNTHESIS_PROMPT,
|
|
13
|
+
NEW_AGENT_PROMPT,
|
|
14
|
+
MODIFY_AGENT_PROMPT,
|
|
17
15
|
)
|
|
18
16
|
from universal_mcp.agents.builder.state import Agent, BuilderState
|
|
19
17
|
from universal_mcp.agents.llm import load_chat_model
|
|
20
18
|
from universal_mcp.agents.shared.tool_node import build_tool_node_graph
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
if old_agent:
|
|
27
|
-
prompt_parts.append(
|
|
28
|
-
"\nThe user wants to modify the following agent design. "
|
|
29
|
-
"Incorporate their feedback into a new design.\n\n"
|
|
30
|
-
f"**User Feedback:** {task}\n\n"
|
|
31
|
-
f"{old_agent.model_dump_json(indent=2)}"
|
|
32
|
-
)
|
|
33
|
-
else:
|
|
34
|
-
prompt_parts.append(f"\n\n**Task:** {task}")
|
|
35
|
-
|
|
36
|
-
prompt = "\n".join(prompt_parts)
|
|
37
|
-
structured_llm = llm.with_structured_output(Agent)
|
|
38
|
-
agent = await structured_llm.ainvoke(prompt)
|
|
39
|
-
return agent
|
|
19
|
+
from universal_mcp.agents.builder.helper import (
|
|
20
|
+
_extract_tools_from_history,
|
|
21
|
+
_clean_conversation_history,
|
|
22
|
+
_merge_tool_configs,
|
|
23
|
+
)
|
|
40
24
|
|
|
41
25
|
|
|
42
26
|
class BuilderAgent(BaseAgent):
|
|
@@ -57,169 +41,174 @@ class BuilderAgent(BaseAgent):
|
|
|
57
41
|
**kwargs,
|
|
58
42
|
)
|
|
59
43
|
self.registry = registry
|
|
60
|
-
self.llm
|
|
44
|
+
self.llm = load_chat_model(model, thinking=False)
|
|
61
45
|
|
|
62
|
-
def
|
|
46
|
+
async def invoke(
|
|
47
|
+
self,
|
|
48
|
+
thread_id: str,
|
|
49
|
+
user_input: dict,
|
|
50
|
+
):
|
|
63
51
|
"""
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
- If an agent has already been generated, it assumes a modification is requested.
|
|
67
|
-
- Otherwise, it starts a fresh build from a text prompt.
|
|
52
|
+
Overrides BaseAgent.invoke to build or modify an agent.
|
|
53
|
+
This is the primary entry point for the Builder Agent.
|
|
68
54
|
"""
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
logger.info("Routing to: modify existing agent.")
|
|
83
|
-
return "synthesize_new_task"
|
|
84
|
-
else:
|
|
85
|
-
logger.info("Routing to: new agent build.")
|
|
86
|
-
return "prepare_for_build"
|
|
87
|
-
|
|
88
|
-
async def _prepare_for_build(self, state: BuilderState):
|
|
89
|
-
"""Sets the initial user task to begin the build process."""
|
|
90
|
-
last_message = state["messages"][-1]
|
|
91
|
-
task = last_message.content
|
|
92
|
-
yield {
|
|
93
|
-
"user_task": task,
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
async def _create_agent(self, state: BuilderState):
|
|
97
|
-
"""Creates or updates the agent definition from a user_task."""
|
|
98
|
-
task = state["user_task"]
|
|
99
|
-
agent = state.get("generated_agent")
|
|
55
|
+
keys = ("userInput", "agent", "tools", "messages")
|
|
56
|
+
userInput, agent_data, tools, messages = (user_input.get(k) for k in keys)
|
|
57
|
+
agent = Agent(**agent_data) if agent_data else None
|
|
58
|
+
|
|
59
|
+
await self.ainit()
|
|
60
|
+
graph = self._graph
|
|
61
|
+
|
|
62
|
+
initial_state = BuilderState(
|
|
63
|
+
user_task=userInput,
|
|
64
|
+
generated_agent=agent,
|
|
65
|
+
tool_config=tools,
|
|
66
|
+
messages=[],
|
|
67
|
+
)
|
|
100
68
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
69
|
+
if messages:
|
|
70
|
+
initial_state["messages"] = [HumanMessage(content=json.dumps(messages))]
|
|
71
|
+
elif not userInput and not agent:
|
|
72
|
+
raise ValueError("Either 'user_input' or 'messages' must be provided for a new agent.")
|
|
105
73
|
|
|
106
|
-
async def _get_tool_config_for_task(self, task: str) -> ToolConfig:
|
|
107
|
-
"""Helper method to find and configure tools for a given task string."""
|
|
108
|
-
tool_finder_graph = build_tool_node_graph(self.llm, self.registry)
|
|
109
|
-
initial_state = {
|
|
110
|
-
"original_task": task,
|
|
111
|
-
"decomposition_attempts": 0,
|
|
112
|
-
}
|
|
113
|
-
final_state = await tool_finder_graph.ainvoke(initial_state)
|
|
114
|
-
execution_plan = final_state.get("execution_plan")
|
|
115
74
|
|
|
116
|
-
|
|
117
|
-
return {}
|
|
75
|
+
run_metadata = { "agent_name": self.name, "is_background_run": False }
|
|
118
76
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
apps_with_tools[app_id].extend(tool_ids)
|
|
125
|
-
|
|
126
|
-
return {app_id: list(set(tools)) for app_id, tools in apps_with_tools.items()}
|
|
127
|
-
|
|
128
|
-
async def _create_tool_config(self, state: BuilderState):
|
|
129
|
-
"""Creates the tool configuration for the agent."""
|
|
130
|
-
task = state["user_task"]
|
|
131
|
-
tool_config = await self._get_tool_config_for_task(task)
|
|
132
|
-
yield {
|
|
133
|
-
"tool_config": tool_config,
|
|
77
|
+
config = {
|
|
78
|
+
"configurable": {"thread_id": thread_id},
|
|
79
|
+
"metadata": run_metadata,
|
|
80
|
+
"run_id": thread_id,
|
|
81
|
+
"run_name" : self.name
|
|
134
82
|
}
|
|
135
83
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
original_task = next((msg.content for msg in state["messages"] if isinstance(msg, HumanMessage)), None)
|
|
139
|
-
modification_request = state["messages"][-1].content
|
|
140
|
-
|
|
141
|
-
if not original_task:
|
|
142
|
-
raise ValueError("Could not find the original task in the conversation history.")
|
|
143
|
-
|
|
144
|
-
synthesis_prompt = TASK_SYNTHESIS_PROMPT.format(
|
|
145
|
-
original_task=original_task,
|
|
146
|
-
modification_request=modification_request,
|
|
147
|
-
)
|
|
148
|
-
|
|
149
|
-
response = await self.llm.ainvoke(synthesis_prompt)
|
|
150
|
-
new_synthesized_task = response.content.strip()
|
|
151
|
-
logger.info(f"The new synthesized task is: {new_synthesized_task}")
|
|
152
|
-
yield {
|
|
153
|
-
"user_task": new_synthesized_task,
|
|
154
|
-
}
|
|
84
|
+
final_state = await graph.ainvoke(initial_state, config=config)
|
|
85
|
+
return final_state
|
|
155
86
|
|
|
156
|
-
|
|
87
|
+
def _entry_point_router(self, state: BuilderState):
|
|
157
88
|
"""
|
|
158
|
-
|
|
159
|
-
This is a one-shot generation.
|
|
89
|
+
Determines the entry point of the graph based on the initial state.
|
|
160
90
|
"""
|
|
161
|
-
|
|
162
|
-
|
|
91
|
+
if state.get("generated_agent"):
|
|
92
|
+
logger.info("Routing to: modify_agent.")
|
|
93
|
+
return "modify_agent"
|
|
94
|
+
else:
|
|
95
|
+
logger.info("Routing to: create_agent.")
|
|
96
|
+
return "create_agent"
|
|
163
97
|
|
|
164
|
-
|
|
165
|
-
|
|
98
|
+
async def _create_agent(self, state: BuilderState):
|
|
99
|
+
"""Generates a new agent profile from scratch."""
|
|
100
|
+
if not state.get("user_task") and not state["messages"]:
|
|
101
|
+
raise ValueError("To create a new agent, provide either a 'user_task' or 'messages'.")
|
|
102
|
+
|
|
103
|
+
user_task = state.get("user_task") or "Not provided"
|
|
104
|
+
conversation_history = []
|
|
105
|
+
|
|
106
|
+
if state["messages"]:
|
|
107
|
+
content_str = state["messages"][-1].content
|
|
108
|
+
raw_history = json.loads(content_str)
|
|
109
|
+
conversation_history = _clean_conversation_history(raw_history)
|
|
110
|
+
|
|
111
|
+
prompt = NEW_AGENT_PROMPT.format(
|
|
112
|
+
user_task=user_task,
|
|
113
|
+
conversation_history=json.dumps(conversation_history, indent=2),
|
|
114
|
+
)
|
|
166
115
|
|
|
167
|
-
|
|
168
|
-
|
|
116
|
+
structured_llm = self.llm.with_structured_output(Agent)
|
|
117
|
+
generated_agent = await structured_llm.ainvoke(prompt)
|
|
118
|
+
|
|
119
|
+
logger.info(f"Successfully created new agent '{generated_agent.name}'.")
|
|
169
120
|
|
|
170
|
-
|
|
121
|
+
return Command(
|
|
122
|
+
update={"generated_agent": generated_agent},
|
|
123
|
+
goto="create_or_update_tool_config",
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
async def _modify_agent(self, state: BuilderState):
|
|
127
|
+
"""Modifies an existing agent based on new user feedback and/or conversation."""
|
|
128
|
+
existing_agent = state["generated_agent"]
|
|
129
|
+
|
|
130
|
+
if not state.get("user_task") and not state["messages"]:
|
|
131
|
+
raise ValueError("To modify an agent, provide either a 'user_task' or 'messages'.")
|
|
132
|
+
|
|
133
|
+
modification_request = state.get("user_task") or "No direct modification request provided."
|
|
134
|
+
|
|
135
|
+
conversation_history = []
|
|
136
|
+
if state["messages"]:
|
|
137
|
+
content_str = state["messages"][-1].content
|
|
138
|
+
raw_history = json.loads(content_str)
|
|
139
|
+
conversation_history = _clean_conversation_history(raw_history)
|
|
140
|
+
|
|
141
|
+
prompt = MODIFY_AGENT_PROMPT.format(
|
|
142
|
+
existing_instructions=existing_agent.instructions,
|
|
143
|
+
modification_request=modification_request,
|
|
171
144
|
conversation_history=json.dumps(conversation_history, indent=2),
|
|
172
|
-
tool_config=json.dumps(tool_config, indent=2),
|
|
173
145
|
)
|
|
174
146
|
|
|
175
147
|
structured_llm = self.llm.with_structured_output(Agent)
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
"
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
148
|
+
modified_agent = await structured_llm.ainvoke(prompt)
|
|
149
|
+
|
|
150
|
+
logger.info(f"Successfully modified agent '{modified_agent.name}'.")
|
|
151
|
+
|
|
152
|
+
return Command(
|
|
153
|
+
update={"generated_agent": modified_agent},
|
|
154
|
+
goto="create_or_update_tool_config"
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
async def _get_tool_config_for_task(self, task: str) -> ToolConfig:
|
|
158
|
+
"""Helper method to find and configure tools for a given task string."""
|
|
159
|
+
if not task:
|
|
160
|
+
return {}
|
|
161
|
+
tool_finder_graph = build_tool_node_graph(self.llm, self.registry)
|
|
162
|
+
final_state = await tool_finder_graph.ainvoke({"original_task": task})
|
|
163
|
+
return final_state.get("execution_plan") or {}
|
|
164
|
+
|
|
165
|
+
async def _create_or_update_tool_config(self, state: BuilderState):
|
|
166
|
+
"""
|
|
167
|
+
Creates or updates the tool configuration by synthesizing tools from multiple sources:
|
|
168
|
+
1. Existing tool config (if any).
|
|
169
|
+
2. Tools extracted from conversation history.
|
|
170
|
+
3. Tools inferred from the agent's primary instructions.
|
|
171
|
+
4. Tools inferred from the user's direct input/task.
|
|
172
|
+
"""
|
|
173
|
+
# 1. Get the existing configuration, if it exists
|
|
174
|
+
final_tool_config = state.get("tool_config") or {}
|
|
175
|
+
|
|
176
|
+
# 2. Extract tools directly from the conversation history
|
|
177
|
+
if state["messages"]:
|
|
178
|
+
content_str = state["messages"][-1].content
|
|
179
|
+
raw_history = json.loads(content_str)
|
|
180
|
+
history_tool_config = _extract_tools_from_history(raw_history)
|
|
181
|
+
final_tool_config = _merge_tool_configs(final_tool_config, history_tool_config)
|
|
182
|
+
|
|
183
|
+
# 3. Find tools based on the agent's synthesized instructions (even if modifying)
|
|
184
|
+
instructions_task = state["generated_agent"].instructions
|
|
185
|
+
instructions_tool_config = await self._get_tool_config_for_task(instructions_task)
|
|
186
|
+
final_tool_config = _merge_tool_configs(final_tool_config, instructions_tool_config)
|
|
187
|
+
|
|
188
|
+
# 4. Find tools based on the direct user input (when creating a new agent)
|
|
189
|
+
user_task = state.get("user_task")
|
|
190
|
+
if user_task:
|
|
191
|
+
user_task_tool_config = await self._get_tool_config_for_task(user_task)
|
|
192
|
+
final_tool_config = _merge_tool_configs(final_tool_config, user_task_tool_config)
|
|
193
|
+
|
|
194
|
+
logger.info(f"Final synthesized tool configuration: {final_tool_config}")
|
|
195
|
+
|
|
196
|
+
return Command(
|
|
197
|
+
update={"tool_config": final_tool_config},
|
|
198
|
+
goto=END,
|
|
199
|
+
)
|
|
187
200
|
|
|
188
201
|
async def _build_graph(self):
|
|
189
202
|
"""Builds the conversational agent graph."""
|
|
190
203
|
builder = StateGraph(BuilderState)
|
|
191
204
|
|
|
192
|
-
# Add nodes
|
|
193
|
-
builder.add_node("prepare_for_build", self._prepare_for_build)
|
|
194
205
|
builder.add_node("create_agent", self._create_agent)
|
|
195
|
-
builder.add_node("
|
|
196
|
-
builder.add_node("
|
|
197
|
-
builder.add_node("synthesize_from_conversation", self._synthesize_from_conversation)
|
|
206
|
+
builder.add_node("modify_agent", self._modify_agent)
|
|
207
|
+
builder.add_node("create_or_update_tool_config", self._create_or_update_tool_config)
|
|
198
208
|
|
|
199
|
-
# The conditional entry point decides the workflow
|
|
200
209
|
builder.add_conditional_edges(
|
|
201
210
|
START,
|
|
202
211
|
self._entry_point_router,
|
|
203
|
-
{
|
|
204
|
-
"prepare_for_build": "prepare_for_build",
|
|
205
|
-
"synthesize_new_task": "synthesize_new_task",
|
|
206
|
-
"synthesize_from_conversation": "synthesize_from_conversation",
|
|
207
|
-
},
|
|
208
212
|
)
|
|
209
213
|
|
|
210
|
-
|
|
211
|
-
builder.add_edge("prepare_for_build", "create_agent")
|
|
212
|
-
builder.add_edge("prepare_for_build", "create_tool_config")
|
|
213
|
-
|
|
214
|
-
# Path for modifying an existing build
|
|
215
|
-
builder.add_edge("synthesize_new_task", "create_agent")
|
|
216
|
-
builder.add_edge("synthesize_new_task", "create_tool_config")
|
|
217
|
-
|
|
218
|
-
# Path for building from conversation ends after its single step
|
|
219
|
-
builder.add_edge("synthesize_from_conversation", END)
|
|
220
|
-
|
|
221
|
-
# Interactive creation nodes lead to the end of the run
|
|
222
|
-
builder.add_edge("create_agent", END)
|
|
223
|
-
builder.add_edge("create_tool_config", END)
|
|
224
|
-
|
|
225
|
-
return builder.compile(checkpointer=self.memory)
|
|
214
|
+
return builder.compile(checkpointer=self.memory)
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
from collections import defaultdict
|
|
2
|
+
import collections
|
|
3
|
+
|
|
4
|
+
from loguru import logger
|
|
5
|
+
from universal_mcp.types import ToolConfig
|
|
6
|
+
|
|
7
|
+
def _extract_tools_from_history(history: list[dict]) -> ToolConfig:
|
|
8
|
+
"""
|
|
9
|
+
Parses a conversation history to find and extract all tool names,
|
|
10
|
+
returning them in a structured ToolConfig format.
|
|
11
|
+
|
|
12
|
+
This function identifies messages with a "type" of "tool", extracts the
|
|
13
|
+
tool's name from the "name" key, and filters out a predefined list of
|
|
14
|
+
excluded tools. The remaining tool names are expected to be in an
|
|
15
|
+
"app_id__tool_id" format. These are then organized into a dictionary
|
|
16
|
+
mapping each app_id to a sorted list of its associated tool_ids.
|
|
17
|
+
"""
|
|
18
|
+
apps_with_tools = collections.defaultdict(set)
|
|
19
|
+
excluded_tools = {"search_tools", "load_tools"}
|
|
20
|
+
|
|
21
|
+
for message in history:
|
|
22
|
+
if message.get("type") == "tool":
|
|
23
|
+
full_tool_name = message.get("name")
|
|
24
|
+
if not full_tool_name or full_tool_name in excluded_tools:
|
|
25
|
+
continue
|
|
26
|
+
|
|
27
|
+
if "__" in full_tool_name:
|
|
28
|
+
app_id, tool_id = full_tool_name.split("__", 1)
|
|
29
|
+
apps_with_tools[app_id].add(tool_id)
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
app_id: sorted(list(tools))
|
|
33
|
+
for app_id, tools in apps_with_tools.items()
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _clean_conversation_history(history: list[dict]) -> list[dict]:
|
|
38
|
+
"""
|
|
39
|
+
Filters a raw conversation history, keeping only messages relevant for
|
|
40
|
+
agent synthesis (human, ai, and tool messages with a name containing double underscores).
|
|
41
|
+
"""
|
|
42
|
+
cleaned_history = []
|
|
43
|
+
for message in history:
|
|
44
|
+
msg_type = message.get("type")
|
|
45
|
+
|
|
46
|
+
if msg_type in ["human", "ai"]:
|
|
47
|
+
cleaned_history.append(message)
|
|
48
|
+
elif msg_type == "tool" and isinstance(message.get("name"), str) and "__" in message["name"]:
|
|
49
|
+
cleaned_history.append(message)
|
|
50
|
+
|
|
51
|
+
return cleaned_history
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _merge_tool_configs(old_config: ToolConfig, new_config: ToolConfig) -> ToolConfig:
|
|
55
|
+
"""Merges two tool configurations, taking the union of tools for each app."""
|
|
56
|
+
if not old_config:
|
|
57
|
+
return new_config
|
|
58
|
+
if not new_config:
|
|
59
|
+
return old_config
|
|
60
|
+
|
|
61
|
+
# Start with a copy of the old configuration
|
|
62
|
+
merged_config = defaultdict(set)
|
|
63
|
+
for app, tools in old_config.items():
|
|
64
|
+
merged_config[app].update(tools)
|
|
65
|
+
|
|
66
|
+
# Add the new tools, ensuring uniqueness
|
|
67
|
+
for app, tools in new_config.items():
|
|
68
|
+
merged_config[app].update(tools)
|
|
69
|
+
|
|
70
|
+
# Convert the sets back to sorted lists for consistent output
|
|
71
|
+
final_config = {app: sorted(list(tool_set)) for app, tool_set in merged_config.items()}
|
|
72
|
+
logger.info(f"Merged tool configuration: {final_config}")
|
|
73
|
+
return final_config
|
|
@@ -1,173 +1,54 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
When given a user's request, immediately follow this structured process:
|
|
5
|
-
|
|
6
|
-
# 1. Intent Breakdown
|
|
7
|
-
- Clearly identify the primary goal the user wants the agent to achieve.
|
|
8
|
-
- Recognize any special requirements, constraints, formatting requests, or interaction rules.
|
|
9
|
-
- Summarize your understanding briefly to ensure alignment with user intent.
|
|
1
|
+
NEW_AGENT_PROMPT = r"""
|
|
2
|
+
# ROLE & GOAL
|
|
3
|
+
You are a specialized Agent Generation AI. Your primary function is to create a complete, high-quality AI agent profile based on the information provided.
|
|
10
4
|
|
|
11
|
-
#
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
- **Expertise**: Precise domain-specific expertise area. Avoid vague or overly general titles.
|
|
15
|
-
- **Instructions**: Compose detailed, highly actionable system instructions that directly command the agent's behavior. Respond in markdown as this text will be rendered in a rich text editor. Write instructions as clear imperatives, without preamble, assuming the agent identity is already established externally.
|
|
16
|
-
- **Schedule**: If the user specifies a schedule, you should also provide a cron expression for the agent to run on. The schedule should be in a proper cron expression and nothing more. Do not respond with any other information or explain your reasoning for the schedule, otherwise this will cause a parsing error that is undesirable.
|
|
5
|
+
# INPUTS
|
|
6
|
+
1. **User Task (Optional):** A brief, initial request from the user. This might be vague or specific.
|
|
7
|
+
2. **Conversation History (Optional):** A transcript of a conversation. This is the **primary source of truth**. If the conversation history is provided, it should be prioritized over the User Task to understand the user's full, potentially multi-step, objective.
|
|
17
8
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
- Outline the exact tasks it handles, specifying expected input/output clearly.
|
|
9
|
+
# INSTRUCTIONS
|
|
10
|
+
Analyze the available inputs to fully understand the user's intent. Synthesize this understanding into a complete agent profile according to the specified JSON schema.
|
|
21
11
|
|
|
22
|
-
|
|
23
|
-
-
|
|
24
|
-
- Include explicit commands, e.g., "Always wrap responses in \`\`\`text\`\`\` blocks.", "Never add greetings or meta-information.", "Always provide outputs in user's requested languages."
|
|
12
|
+
- The first line of the `instructions` field in your output MUST be a single, complete sentence that serves as the definitive task for the agent. This sentence should be synthesized from your analysis of the inputs.
|
|
13
|
+
- The rest of the `instructions` should provide clear, actionable commands for the agent, covering its role, responsibilities, interaction style, and output formatting.
|
|
25
14
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
- Include explicit examples to illustrate correct formatting.
|
|
15
|
+
# TASK
|
|
16
|
+
Based on the following inputs, generate a complete agent profile.
|
|
29
17
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
- Clearly state what the agent must never do or say.
|
|
33
|
-
- Include exact phrases for declining requests outside scope.
|
|
18
|
+
**User Task:**
|
|
19
|
+
{user_task}
|
|
34
20
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
- User's typical request.
|
|
38
|
-
- Final agent response demonstrating perfect compliance.
|
|
21
|
+
**Conversation History:**
|
|
22
|
+
{conversation_history}
|
|
39
23
|
|
|
40
|
-
|
|
24
|
+
**YOUR JSON OUTPUT:**
|
|
41
25
|
"""
|
|
42
26
|
|
|
43
|
-
|
|
44
|
-
TASK_SYNTHESIS_PROMPT = r"""
|
|
27
|
+
MODIFY_AGENT_PROMPT = r"""
|
|
45
28
|
# ROLE & GOAL
|
|
46
|
-
You are
|
|
29
|
+
You are an expert Agent Modification AI. Your task is to intelligently update an existing AI agent's profile by integrating a user's modification request.
|
|
47
30
|
|
|
48
31
|
# CORE PRINCIPLES
|
|
49
|
-
1. **
|
|
50
|
-
2. **
|
|
51
|
-
3. **
|
|
52
|
-
4. **Strict Output Format:** Your output MUST BE ONLY the new synthesized task string. Do not include any preamble, explanation, or quotation marks.
|
|
53
|
-
|
|
54
|
-
---
|
|
55
|
-
# EXAMPLES
|
|
56
|
-
|
|
57
|
-
**EXAMPLE 1: Changing the application for an email task**
|
|
58
|
-
|
|
59
|
-
**Original Task:**
|
|
60
|
-
"Send an email to manoj@agentr.dev with the subject 'Hello' and body 'This is a test of the Gmail agent.' from my Gmail account"
|
|
61
|
-
|
|
62
|
-
**Modification Request:**
|
|
63
|
-
"Please use my Outlook account for this instead of Gmail."
|
|
64
|
-
|
|
65
|
-
**New Synthesized Task:**
|
|
66
|
-
Send an email to manoj@agentr.dev with the subject 'Hello' and body 'This is a test of the Outlook agent.' from my Outlook account
|
|
67
|
-
|
|
68
|
-
---
|
|
69
|
-
**EXAMPLE 2: Modifying the scope and source for a calendar task**
|
|
70
|
-
|
|
71
|
-
**Original Task:**
|
|
72
|
-
"Show me events from today's Google Calendar"
|
|
73
|
-
|
|
74
|
-
**Modification Request:**
|
|
75
|
-
"Actually, I need to see the whole week, not just today. And can you check my Microsoft 365 calendar?"
|
|
76
|
-
|
|
77
|
-
**New Synthesized Task:**
|
|
78
|
-
Show me events for the whole week from my Microsoft 365 calendar
|
|
79
|
-
|
|
80
|
-
---
|
|
81
|
-
**EXAMPLE 3: Changing the target and tool for a web search task**
|
|
82
|
-
|
|
83
|
-
**Original Task:**
|
|
84
|
-
"Find the best restaurants in Goa using exa web search"
|
|
85
|
-
|
|
86
|
-
**Modification Request:**
|
|
87
|
-
"Could you look for hotels instead of restaurants, and please use Perplexity for it."
|
|
88
|
-
|
|
89
|
-
**New Synthesized Task:**
|
|
90
|
-
Find the best hotels in Goa using Perplexity.
|
|
91
|
-
|
|
92
|
-
---
|
|
93
|
-
**EXAMPLE 4: Altering the final action of a multi-step task**
|
|
32
|
+
1. **Synthesize, Don't Just Add:** Do not simply append the modification request. You must seamlessly integrate the user's feedback into the agent's existing instructions, creating a new, coherent set of commands.
|
|
33
|
+
2. **Holistic Update:** The modification may require changes to not only the instructions but also the agent's name, description, and expertise. You must re-evaluate the entire profile.
|
|
34
|
+
3. **Prioritize New Information:** The user's latest modification request and any new conversation history are the primary drivers for the changes.
|
|
94
35
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
**
|
|
99
|
-
"Let's not post anything. Just find the posts and then summarize the key points into a text file for me."
|
|
100
|
-
|
|
101
|
-
**New Synthesized Task:**
|
|
102
|
-
search reddit for posts on elon musk and then summarize the key points into a text file
|
|
103
|
-
|
|
104
|
-
---
|
|
105
|
-
# YOUR TASK
|
|
36
|
+
# INPUTS
|
|
37
|
+
1. **Existing Agent Instructions:** The original instructions that define the agent's current behavior.
|
|
38
|
+
2. **Modification Request:** The user's new input, specifying the desired changes.
|
|
39
|
+
3. **Conversation History (Optional):** A transcript of the conversation that may provide additional context for the modification.
|
|
106
40
|
|
|
107
|
-
|
|
41
|
+
# TASK
|
|
42
|
+
Update the agent profile based on the user's feedback.
|
|
108
43
|
|
|
109
|
-
**
|
|
110
|
-
{
|
|
44
|
+
**Existing Agent Instructions:**
|
|
45
|
+
{existing_instructions}
|
|
111
46
|
|
|
112
47
|
**Modification Request:**
|
|
113
48
|
{modification_request}
|
|
114
49
|
|
|
115
|
-
**
|
|
116
|
-
"""
|
|
117
|
-
|
|
118
|
-
AGENT_FROM_CONVERSATION_PROMPT = r"""
|
|
119
|
-
# ROLE & GOAL
|
|
120
|
-
You are a highly intelligent 'Agent Analyst' AI. Your sole purpose is to analyze a raw conversation transcript between a user and an AI assistant and a definitive list of tools the assistant used. From this data, you must synthesize a complete, reusable AI agent profile.
|
|
121
|
-
|
|
122
|
-
# INPUTS
|
|
123
|
-
1. **Conversation History:** A transcript of the dialogue.
|
|
124
|
-
2. **Used Tools:** A definitive list of tool configurations (`{{app_id: [tool_names]}}`) that were successfully used to fulfill the user's requests in the conversation.
|
|
125
|
-
|
|
126
|
-
# 1. Intent Breakdown
|
|
127
|
-
- Clearly identify the primary goal the user wants the agent to achieve.
|
|
128
|
-
- Recognize any special requirements, constraints, formatting requests, or interaction rules.
|
|
129
|
-
- Summarize your understanding briefly to ensure alignment with user intent.
|
|
130
|
-
|
|
131
|
-
# 2. Agent Profile Definition
|
|
132
|
-
- **Name (2-4 words)**: Concise, clear, and memorable name reflecting core functionality.
|
|
133
|
-
- **Description (1-2 sentences)**: Captures the unique value and primary benefit to users.
|
|
134
|
-
- **Expertise**: Precise domain-specific expertise area. Avoid vague or overly general titles.
|
|
135
|
-
- **Instructions**: Compose detailed, highly actionable system instructions that directly command the agent's behavior. Respond in markdown as this text will be rendered in a rich text editor. Write instructions as clear imperatives, without preamble, assuming the agent identity is already established externally.
|
|
136
|
-
- **Schedule**: If the user specifies a schedule, you should also provide a cron expression for the agent to run on. The schedule should be in a proper cron expression and nothing more. Do not respond with any other information or explain your reasoning for the schedule, otherwise this will cause a parsing error that is undesirable.
|
|
137
|
-
|
|
138
|
-
## ROLE & RESPONSIBILITY
|
|
139
|
-
- Clearly state the agent's primary mission, e.g., "Your primary mission is...", "Your core responsibility is...".
|
|
140
|
-
- Outline the exact tasks it handles, specifying expected input/output clearly.
|
|
141
|
-
|
|
142
|
-
## INTERACTION STYLE
|
|
143
|
-
- Define exactly how to communicate with users: tone, format, response structure.
|
|
144
|
-
- Include explicit commands, e.g., "Always wrap responses in \`\`\`text\`\`\` blocks.", "Never add greetings or meta-information.", "Always provide outputs in user's requested languages."
|
|
145
|
-
|
|
146
|
-
## OUTPUT FORMATTING RULES
|
|
147
|
-
- Clearly specify formatting standards required by the user (e.g., JSON, plain text, markdown).
|
|
148
|
-
- Include explicit examples to illustrate correct formatting.
|
|
149
|
-
|
|
150
|
-
## LIMITATIONS & CONSTRAINTS
|
|
151
|
-
- Explicitly define boundaries of the agent's capabilities.
|
|
152
|
-
- Clearly state what the agent must never do or say.
|
|
153
|
-
- Include exact phrases for declining requests outside scope.
|
|
154
|
-
|
|
155
|
-
## REAL-WORLD EXAMPLES
|
|
156
|
-
Provide two explicit interaction examples showing:
|
|
157
|
-
- User's typical request.
|
|
158
|
-
- Final agent response demonstrating perfect compliance.
|
|
159
|
-
|
|
160
|
-
Create an agent that feels thoughtfully designed, intelligent, and professionally reliable, perfectly matched to the user's original intent.
|
|
161
|
-
|
|
162
|
-
# YOUR TASK
|
|
163
|
-
|
|
164
|
-
Now, perform this analysis for the following inputs.
|
|
165
|
-
|
|
166
|
-
**INPUT - Conversation History:**
|
|
50
|
+
**Conversation History:**
|
|
167
51
|
{conversation_history}
|
|
168
52
|
|
|
169
|
-
**
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
**YOUR JSON OUTPUT:**
|
|
173
|
-
"""
|
|
53
|
+
**YOUR UPDATED JSON OUTPUT:**
|
|
54
|
+
"""
|