|
| 1 | +from typing import Any, Optional |
| 2 | +from arkitect.core.component.context.context import Context |
| 3 | +from arkitect.core.component.context.model import State |
| 4 | +from arkitect.core.component.tool.builder import build_mcp_clients_from_config |
| 5 | + |
| 6 | +from arkitect.core.component.context.hooks import ( |
| 7 | + PreToolCallHook, |
| 8 | + PostToolCallHook, |
| 9 | +) |
| 10 | + |
| 11 | +CONFIG_FILE_PATH = "./mcp_config.json" |
| 12 | + |
| 13 | + |
| 14 | +class MyHooks(PreToolCallHook, PostToolCallHook): |
| 15 | + async def pre_tool_call( |
| 16 | + self, |
| 17 | + name: str, |
| 18 | + arguments: str, |
| 19 | + state: State, |
| 20 | + ) -> State: |
| 21 | + print("\n" + "=" * 20 + "Inside pre tool call" + "=" * 20 + "\n") |
| 22 | + last_assistant_message = state.messages[-1] |
| 23 | + tool_call_part = last_assistant_message["tool_calls"] |
| 24 | + for tool_call in tool_call_part: |
| 25 | + print( |
| 26 | + f"Tool {tool_call['function']['name']} with {tool_call['function']['arguments']}" |
| 27 | + ) |
| 28 | + # you may modify this or ask users for approval here |
| 29 | + return state # return state no matter if have modified it |
| 30 | + |
| 31 | + async def post_tool_call( |
| 32 | + self, |
| 33 | + name: str, |
| 34 | + arguments: str, |
| 35 | + response: Any, |
| 36 | + exception: Optional[Exception], |
| 37 | + state: State, |
| 38 | + ) -> State: |
| 39 | + print("\n" + "=" * 20 + "Inside post tool call" + "=" * 20 + "\n") |
| 40 | + print(f"Tool {name} with {arguments} returned {response}") |
| 41 | + return state # return state no matter if have modified it |
| 42 | + |
| 43 | + |
| 44 | +class Agent: |
| 45 | + def __init__(self, mcp_config_file: str): |
| 46 | + # Initialize session and client objects |
| 47 | + self.mcp_config_file = mcp_config_file |
| 48 | + |
| 49 | + async def process_query(self, query: str) -> str: |
| 50 | + mcp_clients, cleanup = build_mcp_clients_from_config(self.mcp_config_file) |
| 51 | + messages = [ |
| 52 | + { |
| 53 | + "role": "user", |
| 54 | + "content": query, |
| 55 | + } |
| 56 | + ] |
| 57 | + |
| 58 | + # Initialize LLM |
| 59 | + ctx = Context( |
| 60 | + model="deepseek-v3-241226", |
| 61 | + tools=list( |
| 62 | + mcp_clients.values() |
| 63 | + ), # 直接在这个list里传入你的所有的python方法或者MCPClient,可以混着传入 |
| 64 | + ) |
| 65 | + my_hook = MyHooks() |
| 66 | + ctx.set_pre_tool_call_hook(my_hook) |
| 67 | + ctx.set_post_tool_call_hook(my_hook) |
| 68 | + await ctx.init() |
| 69 | + |
| 70 | + completion = await ctx.completions.create(messages, stream=True) |
| 71 | + async for chunk in completion: |
| 72 | + if chunk.choices: |
| 73 | + print(chunk.choices[0].delta.content, end="") |
| 74 | + await cleanup() # 注意cleanup!!! |
| 75 | + |
| 76 | + async def chat_loop(self): |
| 77 | + """Run an interactive chat loop""" |
| 78 | + print("\nMCP Client Started!") |
| 79 | + print("Type your queries or 'quit' to exit.") |
| 80 | + |
| 81 | + while True: |
| 82 | + try: |
| 83 | + query = input("\nQuery: ").strip() |
| 84 | + |
| 85 | + if query.lower() == "quit": |
| 86 | + break |
| 87 | + |
| 88 | + await self.process_query(query) |
| 89 | + |
| 90 | + except Exception as e: |
| 91 | + print(f"\nError: {str(e)}") |
| 92 | + |
| 93 | + |
| 94 | +async def main(): |
| 95 | + agent = Agent(mcp_config_file=CONFIG_FILE_PATH) |
| 96 | + await agent.chat_loop() |
| 97 | + |
| 98 | + |
| 99 | +if __name__ == "__main__": |
| 100 | + import asyncio |
| 101 | + |
| 102 | + asyncio.run(main()) |
0 commit comments