预构建的CodeAct智能体与LlamaIndex
LlamaIndex 提供了一个预构建的 CodeAct 智能体,可用于编写和执行代码,灵感源自原始的 CodeAct 论文。
通过此智能体,您可以为智能体提供一组函数,智能体将编写使用这些函数的代码来帮助完成您赋予它的任务。
使用 CodeAct 智能体的一些优势:
- 无需详尽列出智能体可能需要的所有函数
- 智能体可以围绕您现有的函数开发复杂的工作流
- 可直接与现有API集成
让我们通过一个简单的示例来了解如何使用 CodeAct 智能体。
注意: 此示例包含会执行任意代码的程序。这具有危险性,在生产环境中应使用适当的沙箱保护机制。
首先,让我们配置要使用的大语言模型,并提供一些可在代码中使用的函数。
%pip install -U llama-index-core llama-index-llms-ollamafrom llama_index.llms.openai import OpenAI
# Configure the LLMllm = OpenAI(model="gpt-4o-mini", api_key="sk-...")
# Define a few helper functionsdef add(a: int, b: int) -> int: """Add two numbers together""" return a + b
def subtract(a: int, b: int) -> int: """Subtract two numbers""" return a - b
def multiply(a: int, b: int) -> int: """Multiply two numbers""" return a * b
def divide(a: int, b: int) -> float: """Divide two numbers""" return a / bCodeActAgent 将需要特定的 code_execute_fn 来执行由智能体生成的代码。
下面,我们定义一个简单的 code_execute_fn,它将在进程中执行代码并维护执行状态。
注意:在生产环境中,您应该使用更健壮的代码执行方法。这里仅用于演示目的,在进程内执行代码存在风险。建议考虑使用Docker或外部服务来执行代码。
通过这个执行器,我们可以在执行上下文中传入一个包含局部和全局变量的字典。
locals: 执行上下文中使用的局部变量,包括我们希望大语言模型围绕编码的函数globals: 在执行上下文中使用的全局变量,包括我们想要在执行上下文中使用的内置函数和其他导入模块
from typing import Any, Dict, Tupleimport ioimport contextlibimport astimport traceback
class SimpleCodeExecutor: """ A simple code executor that runs Python code with state persistence.
This executor maintains a global and local state between executions, allowing for variables to persist across multiple code runs.
NOTE: not safe for production use! Use with caution. """
def __init__(self, locals: Dict[str, Any], globals: Dict[str, Any]): """ Initialize the code executor.
Args: locals: Local variables to use in the execution context globals: Global variables to use in the execution context """ # State that persists between executions self.globals = globals self.locals = locals
def execute(self, code: str) -> Tuple[bool, str, Any]: """ Execute Python code and capture output and return values.
Args: code: Python code to execute
Returns: Dict with keys `success`, `output`, and `return_value` """ # Capture stdout and stderr stdout = io.StringIO() stderr = io.StringIO()
output = "" return_value = None try: # Execute with captured output with contextlib.redirect_stdout( stdout ), contextlib.redirect_stderr(stderr): # Try to detect if there's a return value (last expression) try: tree = ast.parse(code) last_node = tree.body[-1] if tree.body else None
# If the last statement is an expression, capture its value if isinstance(last_node, ast.Expr): # Split code to add a return value assignment last_line = code.rstrip().split("\n")[-1] exec_code = ( code[: -len(last_line)] + "\n__result__ = " + last_line )
# Execute modified code exec(exec_code, self.globals, self.locals) return_value = self.locals.get("__result__") else: # Normal execution exec(code, self.globals, self.locals) except: # If parsing fails, just execute the code as is exec(code, self.globals, self.locals)
# Get output output = stdout.getvalue() if stderr.getvalue(): output += "\n" + stderr.getvalue()
except Exception as e: # Capture exception information output = f"Error: {type(e).__name__}: {str(e)}\n" output += traceback.format_exc()
if return_value is not None: output += "\n\n" + str(return_value)
return outputcode_executor = SimpleCodeExecutor( # give access to our functions defined above locals={ "add": add, "subtract": subtract, "multiply": multiply, "divide": divide, }, globals={ # give access to all builtins "__builtins__": __builtins__, # give access to numpy "np": __import__("numpy"), },)设置 CodeAct 智能体
Section titled “Setup the CodeAct Agent”现在我们有了代码执行器,就可以设置 CodeAct 智能体了。
from llama_index.core.agent.workflow import CodeActAgentfrom llama_index.core.workflow import Context
agent = CodeActAgent( code_execute_fn=code_executor.execute, llm=llm, tools=[add, subtract, multiply, divide],)
# context to hold the agent's session/state/chat historyctx = Context(agent)现在我们有了智能体,就可以用它来完成任务!由于我们为它提供了一些数学函数,我们将提示它执行需要计算的任务。
from llama_index.core.agent.workflow import ( ToolCall, ToolCallResult, AgentStream,)
async def run_agent_verbose(agent, ctx, query): handler = agent.run(query, ctx=ctx) print(f"User: {query}") async for event in handler.stream_events(): if isinstance(event, ToolCallResult): print( f"\n-----------\nCode execution result:\n{event.tool_output}" ) elif isinstance(event, ToolCall): print(f"\n-----------\nParsed code:\n{event.tool_kwargs['code']}") elif isinstance(event, AgentStream): print(f"{event.delta}", end="", flush=True)
return await handler在这里,智能体使用一些内置函数来计算从1到10所有数字的总和。
response = await run_agent_verbose( agent, ctx, "Calculate the sum of all numbers from 1 to 10")User: Calculate the sum of all numbers from 1 to 10The sum of all numbers from 1 to 10 can be calculated using the formula for the sum of an arithmetic series. However, I will compute it directly for you.
<execute># Calculate the sum of numbers from 1 to 10total_sum = sum(range(1, 11))print(total_sum)</execute>-----------Parsed code:# Calculate the sum of numbers from 1 to 10total_sum = sum(range(1, 11))print(total_sum)
-----------Code execution result:55
The sum of all numbers from 1 to 10 is 55.接下来,我们让智能体使用我们传入的工具。
response = await run_agent_verbose( agent, ctx, "Add 5 and 3, then multiply the result by 2")User: Add 5 and 3, then multiply the result by 2I will perform the addition of 5 and 3, and then multiply the result by 2.
<execute># Perform the calculationaddition_result = add(5, 3)final_result = multiply(addition_result, 2)print(final_result)</execute>-----------Parsed code:# Perform the calculationaddition_result = add(5, 3)final_result = multiply(addition_result, 2)print(final_result)
-----------Code execution result:16
The result of adding 5 and 3, then multiplying by 2, is 16.我们甚至可以让智能体为我们定义新函数!
response = await run_agent_verbose( agent, ctx, "Calculate the sum of the first 10 fibonacci numbers")User: Calculate the sum of the first 10 fibonacci numbersI will calculate the sum of the first 10 Fibonacci numbers.
<execute>def fibonacci(n): fib_sequence = [0, 1] for i in range(2, n): fib_sequence.append(fib_sequence[-1] + fib_sequence[-2]) return fib_sequence
# Calculate the sum of the first 10 Fibonacci numbersfirst_10_fib = fibonacci(10)fibonacci_sum = sum(first_10_fib)print(fibonacci_sum)</execute>-----------Parsed code:def fibonacci(n): fib_sequence = [0, 1] for i in range(2, n): fib_sequence.append(fib_sequence[-1] + fib_sequence[-2]) return fib_sequence
# Calculate the sum of the first 10 Fibonacci numbersfirst_10_fib = fibonacci(10)fibonacci_sum = sum(first_10_fib)print(fibonacci_sum)
-----------Code execution result:88
The sum of the first 10 Fibonacci numbers is 88.然后在新任务中重用这些新函数!
response = await run_agent_verbose( agent, ctx, "Calculate the sum of the first 20 fibonacci numbers")User: Calculate the sum of the first 20 fibonacci numbersI will calculate the sum of the first 20 Fibonacci numbers.
<execute># Calculate the sum of the first 20 Fibonacci numbersfirst_20_fib = fibonacci(20)fibonacci_sum_20 = sum(first_20_fib)print(fibonacci_sum_20)</execute>-----------Parsed code:# Calculate the sum of the first 20 Fibonacci numbersfirst_20_fib = fibonacci(20)fibonacci_sum_20 = sum(first_20_fib)print(fibonacci_sum_20)
-----------Code execution result:10945
The sum of the first 20 Fibonacci numbers is 10,945.