You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

software_consultancy.py 13 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. """This is an example demonstrates event-driven orchestration using a
  2. group chat manager agnent.
  3. WARNING: do not run this example in your local machine as it involves
  4. executing arbitrary code. Use a secure environment like a docker container
  5. or GitHub Codespaces to run this example.
  6. """
  7. import argparse
  8. import asyncio
  9. import base64
  10. import logging
  11. import os
  12. import sys
  13. import aiofiles
  14. import aiohttp
  15. import openai
  16. from agnext.application import SingleThreadedAgentRuntime
  17. from agnext.components.models import SystemMessage
  18. from agnext.components.tools import FunctionTool
  19. from agnext.core import AgentInstantiationContext, AgentRuntime
  20. from markdownify import markdownify # type: ignore
  21. from tqdm import tqdm
  22. from typing_extensions import Annotated
  23. sys.path.append(os.path.abspath(os.path.dirname(__file__)))
  24. sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
  25. from agnext.core import AgentId
  26. from common.agents import ChatCompletionAgent
  27. from common.memory import HeadAndTailChatMemory
  28. from common.patterns._group_chat_manager import GroupChatManager
  29. from common.utils import get_chat_completion_client_from_envs
  30. from utils import TextualChatApp, TextualUserAgent
  31. async def write_file(filename: str, content: str) -> str:
  32. async with aiofiles.open(filename, "w") as file:
  33. await file.write(content)
  34. return f"Content written to {filename}."
  35. async def execute_command(command: str) -> Annotated[str, "The standard output and error of the executed command."]:
  36. process = await asyncio.subprocess.create_subprocess_shell(
  37. command,
  38. stdout=asyncio.subprocess.PIPE,
  39. stderr=asyncio.subprocess.PIPE,
  40. )
  41. stdout, stderr = await process.communicate()
  42. return f"stdout: {stdout.decode()}\nstderr: {stderr.decode()}"
  43. async def read_file(filename: str) -> Annotated[str, "The content of the file."]:
  44. async with aiofiles.open(filename, "r") as file:
  45. return await file.read()
  46. async def remove_file(filename: str) -> str:
  47. process = await asyncio.subprocess.create_subprocess_exec("rm", filename)
  48. await process.wait()
  49. if process.returncode != 0:
  50. raise ValueError(f"Error occurred while removing file: {filename}")
  51. return f"File removed: {filename}."
  52. async def list_files(directory: str) -> Annotated[str, "The list of files in the directory."]:
  53. # Ask for confirmation first.
  54. # await confirm(f"Are you sure you want to list files in {directory}?")
  55. process = await asyncio.subprocess.create_subprocess_exec(
  56. "ls",
  57. directory,
  58. stdout=asyncio.subprocess.PIPE,
  59. stderr=asyncio.subprocess.PIPE,
  60. )
  61. stdout, stderr = await process.communicate()
  62. if stderr:
  63. raise ValueError(f"Error occurred while listing files: {stderr.decode()}")
  64. return stdout.decode()
  65. async def browse_web(url: str) -> Annotated[str, "The content of the web page in Markdown format."]:
  66. async with aiohttp.ClientSession() as session:
  67. async with session.get(url) as response:
  68. html = await response.text()
  69. markdown = markdownify(html) # type: ignore
  70. if isinstance(markdown, str):
  71. return markdown
  72. return f"Unable to parse content from {url}."
  73. async def create_image(
  74. description: Annotated[str, "Describe the image to create"],
  75. filename: Annotated[str, "The path to save the created image"],
  76. ) -> str:
  77. # Use Dalle to generate an image from the description.
  78. with tqdm(desc="Generating image...", leave=False) as pbar:
  79. client = openai.AsyncClient()
  80. response = await client.images.generate(model="dall-e-2", prompt=description, response_format="b64_json")
  81. pbar.close()
  82. assert len(response.data) > 0 and response.data[0].b64_json is not None
  83. # Save the image to a file.
  84. async with aiofiles.open(filename, "wb") as file:
  85. image_data = base64.b64decode(response.data[0].b64_json)
  86. await file.write(image_data)
  87. return f"Image created and saved to {filename}."
  88. async def software_consultancy(runtime: AgentRuntime, app: TextualChatApp) -> None: # type: ignore
  89. await runtime.register(
  90. "Customer",
  91. lambda: TextualUserAgent(
  92. description="A customer looking for help.",
  93. app=app,
  94. ),
  95. )
  96. await runtime.register(
  97. "Developer",
  98. lambda: ChatCompletionAgent(
  99. description="A Python software developer.",
  100. system_messages=[
  101. SystemMessage(
  102. "Your are a Python developer. \n"
  103. "You can read, write, and execute code. \n"
  104. "You can browse files and directories. \n"
  105. "You can also browse the web for documentation. \n"
  106. "You are entering a work session with the customer, product manager, UX designer, and illustrator. \n"
  107. "When you are given a task, you should immediately start working on it. \n"
  108. "Be concise and deliver now."
  109. )
  110. ],
  111. model_client=get_chat_completion_client_from_envs(model="gpt-4-turbo"),
  112. memory=HeadAndTailChatMemory(head_size=1, tail_size=10),
  113. tools=[
  114. FunctionTool(
  115. write_file,
  116. name="write_file",
  117. description="Write code to a file.",
  118. ),
  119. FunctionTool(
  120. read_file,
  121. name="read_file",
  122. description="Read code from a file.",
  123. ),
  124. FunctionTool(
  125. execute_command,
  126. name="execute_command",
  127. description="Execute a unix shell command.",
  128. ),
  129. FunctionTool(list_files, name="list_files", description="List files in a directory."),
  130. FunctionTool(browse_web, name="browse_web", description="Browse a web page."),
  131. ],
  132. tool_approver=AgentId("Customer", AgentInstantiationContext.current_agent_id().key),
  133. ),
  134. )
  135. await runtime.register(
  136. "ProductManager",
  137. lambda: ChatCompletionAgent(
  138. description="A product manager. "
  139. "Responsible for interfacing with the customer, planning and managing the project. ",
  140. system_messages=[
  141. SystemMessage(
  142. "You are a product manager. \n"
  143. "You can browse files and directories. \n"
  144. "You are entering a work session with the customer, developer, UX designer, and illustrator. \n"
  145. "Keep the project on track. Don't hire any more people. \n"
  146. "When a milestone is reached, stop and ask for customer feedback. Make sure the customer is satisfied. \n"
  147. "Be VERY concise."
  148. )
  149. ],
  150. model_client=get_chat_completion_client_from_envs(model="gpt-4-turbo"),
  151. memory=HeadAndTailChatMemory(head_size=1, tail_size=10),
  152. tools=[
  153. FunctionTool(
  154. read_file,
  155. name="read_file",
  156. description="Read from a file.",
  157. ),
  158. FunctionTool(list_files, name="list_files", description="List files in a directory."),
  159. FunctionTool(browse_web, name="browse_web", description="Browse a web page."),
  160. ],
  161. tool_approver=AgentId("Customer", AgentInstantiationContext.current_agent_id().key),
  162. ),
  163. )
  164. await runtime.register(
  165. "UserExperienceDesigner",
  166. lambda: ChatCompletionAgent(
  167. description="A user experience designer for creating user interfaces.",
  168. system_messages=[
  169. SystemMessage(
  170. "You are a user experience designer. \n"
  171. "You can create user interfaces from descriptions. \n"
  172. "You can browse files and directories. \n"
  173. "You are entering a work session with the customer, developer, product manager, and illustrator. \n"
  174. "When you are given a task, you should immediately start working on it. \n"
  175. "Be concise and deliver now."
  176. )
  177. ],
  178. model_client=get_chat_completion_client_from_envs(model="gpt-4-turbo"),
  179. memory=HeadAndTailChatMemory(head_size=1, tail_size=10),
  180. tools=[
  181. FunctionTool(
  182. write_file,
  183. name="write_file",
  184. description="Write code to a file.",
  185. ),
  186. FunctionTool(
  187. read_file,
  188. name="read_file",
  189. description="Read code from a file.",
  190. ),
  191. FunctionTool(list_files, name="list_files", description="List files in a directory."),
  192. ],
  193. tool_approver=AgentId("Customer", AgentInstantiationContext.current_agent_id().key),
  194. ),
  195. )
  196. await runtime.register(
  197. "Illustrator",
  198. lambda: ChatCompletionAgent(
  199. description="An illustrator for creating images.",
  200. system_messages=[
  201. SystemMessage(
  202. "You are an illustrator. "
  203. "You can create images from descriptions. "
  204. "You are entering a work session with the customer, developer, product manager, and UX designer. \n"
  205. "When you are given a task, you should immediately start working on it. \n"
  206. "Be concise and deliver now."
  207. )
  208. ],
  209. model_client=get_chat_completion_client_from_envs(model="gpt-4-turbo"),
  210. memory=HeadAndTailChatMemory(head_size=1, tail_size=10),
  211. tools=[
  212. FunctionTool(
  213. create_image,
  214. name="create_image",
  215. description="Create an image from a description.",
  216. ),
  217. ],
  218. tool_approver=AgentId("Customer", AgentInstantiationContext.current_agent_id().key),
  219. ),
  220. )
  221. await runtime.register(
  222. "GroupChatManager",
  223. lambda: GroupChatManager(
  224. description="A group chat manager.",
  225. memory=HeadAndTailChatMemory(head_size=1, tail_size=10),
  226. model_client=get_chat_completion_client_from_envs(model="gpt-4-turbo"),
  227. participants=[
  228. AgentId("Developer", AgentInstantiationContext.current_agent_id().key),
  229. AgentId("ProductManager", AgentInstantiationContext.current_agent_id().key),
  230. AgentId("UserExperienceDesigner", AgentInstantiationContext.current_agent_id().key),
  231. AgentId("Illustrator", AgentInstantiationContext.current_agent_id().key),
  232. AgentId("Customer", AgentInstantiationContext.current_agent_id().key),
  233. ],
  234. ),
  235. )
  236. art = r"""
  237. +----------------------------------------------------------+
  238. | ____ __ _ |
  239. | / ___| ___ / _| |___ ____ _ _ __ ___ |
  240. | \___ \ / _ \| |_| __\ \ /\ / / _` | '__/ _ \ |
  241. | ___) | (_) | _| |_ \ V V / (_| | | | __/ |
  242. | |____/ \___/|_| \__| \_/\_/ \__,_|_| \___| |
  243. | |
  244. | ____ _ _ |
  245. | / ___|___ _ __ ___ _ _| | |_ __ _ _ __ ___ _ _ |
  246. | | | / _ \| '_ \/ __| | | | | __/ _` | '_ \ / __| | | | |
  247. | | |__| (_) | | | \__ \ |_| | | || (_| | | | | (__| |_| | |
  248. | \____\___/|_| |_|___/\__,_|_|\__\__,_|_| |_|\___|\__, | |
  249. | |___/ |
  250. | |
  251. | Work with a software development consultancy to create |
  252. | your own Python application. You are working with a team |
  253. | of the following agents: |
  254. | 1. 🤖 Developer: A Python software developer. |
  255. | 2. 🤖 ProductManager: A product manager. |
  256. | 3. 🤖 UserExperienceDesigner: A user experience designer. |
  257. | 4. 🤖 Illustrator: An illustrator. |
  258. +----------------------------------------------------------+
  259. """
  260. app.welcoming_notice = art
  261. async def main() -> None:
  262. runtime = SingleThreadedAgentRuntime()
  263. app = TextualChatApp(runtime, user_name="You")
  264. await software_consultancy(runtime, app)
  265. # Start the runtime.
  266. runtime.start()
  267. # Start the app.
  268. await app.run_async()
  269. if __name__ == "__main__":
  270. parser = argparse.ArgumentParser(description="Software consultancy demo.")
  271. parser.add_argument("--verbose", action="store_true", help="Enable verbose logging.")
  272. args = parser.parse_args()
  273. if args.verbose:
  274. logging.basicConfig(level=logging.WARNING)
  275. logging.getLogger("agnext").setLevel(logging.DEBUG)
  276. handler = logging.FileHandler("software_consultancy.log")
  277. logging.getLogger("agnext").addHandler(handler)
  278. asyncio.run(main())