多角色AI代理的一次尝试- AI代码助手

AI Agent在投资领域比较火爆,代码助手的投资也比较多,单AI Agent,多角色AI Agent逐渐会成为AI应用的方向。最近一个10岁孩子直接使用豆包和两个ChatGPT进行愚公该移山还是该搬家的辩论活动吸引了大家的眼球,用户作为主持人角色指导2个GPT进行正反方辩友进行辩论和1个GPT作为评委进行评价,这个就是典型的多角色AI代理的应用场景,使用了3个GPT,其实一个也可以的,虽然这个活动最终没有达到预期,Chat GPT作为评委的代理,虽然没有评论,但都记录了它"听"到的内容。这个场景是直接使用GPT作为代理的例子,多角色AI Agent打开走向AGI的想象空间。下面以我比较熟悉的领域,尝试实现一个多角色AI代理编写代码的例子。涉及的角色为:

用户UserInterface:输入任务指令,并在任务无法完成时,进行指导,如:修改代码。

需求分析RequirementAnalyzer:针对用户输入的指令进行需求分析。

代码结构ProgramStructure:针对需求,提出合理的代码结构。

编写代码CodeGenerator:根据需求及代码结构和语言设置,编写代码。

代码审查CodeReviewer:针对生成的代码进行代码审查,提出改进意见。

代码执行CodeExecutor:执行审查完成的代码,输出测试结果。

协作交换Coordinator:协作交换逻辑,通过多次迭代完成任务。

完整代码:

复制代码
import gradio as gr
import requests
import subprocess
import logging
import json
from io import StringIO
import threading
import time

# 配置日志记录
log_stream = StringIO()
logging.basicConfig(
    level=logging.DEBUG,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.StreamHandler(),  # 输出到控制台
        logging.StreamHandler(log_stream)  # 输出到 StringIO 流
    ]
)

# 定义RequirementAnalyzer角色
class RequirementAnalyzer:
    def __init__(self, api_url, model):
        self.api_url = api_url
        self.model = model

    def analyze_instruction(self, instruction):
        messages = [
            {"role": "system", "content": "你是一个有帮助的助手。"},
            {"role": "user", "content": f"分析以下指令: {instruction}"}
        ]
        response = requests.post(self.api_url, json={"messages": messages, "model": self.model, "stream": True}, stream=True)
        analysis = self.process_stream(response)
        logging.info(f"[RequirementAnalyzer] 分析的需求: {analysis}")
        return analysis

    def process_stream(self, response):
        content = ""
        for chunk in response.iter_content(chunk_size=None):
            if chunk:
                chunk_data = chunk.decode('utf-8')
                if "done" in chunk_data:
                    chunk_data = chunk_data.split("data: ")[-1]
                    chunk_json = json.loads(chunk_data)
                    if chunk_json["done"]:
                        break
                    else:
                        content += chunk_json["message"]["content"]
        return content

# 定义ProgramStructure角色
class ProgramStructure:
    def __init__(self, api_url, model):
        self.api_url = api_url
        self.model = model

    def determine_structure(self, requirement):
        messages = [
            {"role": "system", "content": "你是一个有帮助的助手。"},
            {"role": "user", "content": f"确定以下需求的类结构: {requirement}"}
        ]
        response = requests.post(self.api_url, json={"messages": messages, "model": self.model, "stream": True}, stream=True)
        structure = self.process_stream(response)
        logging.info(f"[ProgramStructure] 确定的结构: {structure}")
        return structure

    def process_stream(self, response):
        content = ""
        for chunk in response.iter_content(chunk_size=None):
            if chunk:
                chunk_data = chunk.decode('utf-8')
                if "done" in chunk_data:
                    chunk_data = chunk_data.split("data: ")[-1]
                    chunk_json = json.loads(chunk_data)
                    if chunk_json["done"]:
                        break
                    else:
                        content += chunk_json["message"]["content"]
        return content

# 定义CodeGenerator角色
class CodeGenerator:
    def __init__(self, api_url, model):
        self.api_url = api_url
        self.model = model

    def generate_code(self, requirement, structure, language):
        messages = [
            {"role": "system", "content": "你是一个有帮助的助手。"},
            {"role": "user", "content": f"根据以下需求和结构编写{language}代码: {requirement}, {structure}"}
        ]
        response = requests.post(self.api_url, json={"messages": messages, "model": self.model, "stream": True}, stream=True)
        code = self.process_stream(response)
        code = self.extract_code(code)  # 提取代码部分
        logging.info(f"[CodeGenerator] 生成的代码: {code}")
        return code

    def process_stream(self, response):
        content = ""
        for chunk in response.iter_content(chunk_size=None):
            if chunk:
                chunk_data = chunk.decode('utf-8')
                if "done" in chunk_data:
                    chunk_data = chunk_data.split("data: ")[-1]
                    chunk_json = json.loads(chunk_data)
                    if chunk_json["done"]:
                        break
                    else:
                        content += chunk_json["message"]["content"]
        return content

    def extract_code(self, content):
        # 假设代码部分被包裹在 ``` 代码块中,并去掉打头的语言标识
        import re
        match = re.search(r'```.*?\n(.*?)```', content, re.DOTALL)
        if match:
            return match.group(1).strip()
        return content.strip()

# 定义CodeReviewer角色
class CodeReviewer:
    def __init__(self, api_url, model):
        self.api_url = api_url
        self.model = model

    def review_code(self, code, language):
        messages = [
            {"role": "system", "content": "你是一个有帮助的助手。"},
            {"role": "user", "content": f"审查以下{language}代码并提出改进建议,并给出结论,如:整体来说,这段代码已经很完善: {code}"}
        ]
        response = requests.post(self.api_url, json={"messages": messages, "model": self.model, "stream": True}, stream=True)
        review = self.process_stream(response)
        logging.info(f"[CodeReviewer] 代码审查: {review}")
        return review

    def process_stream(self, response):
        content = ""
        for chunk in response.iter_content(chunk_size=None):
            if chunk:
                chunk_data = chunk.decode('utf-8')
                if "done" in chunk_data:
                    chunk_data = chunk_data.split("data: ")[-1]
                    chunk_json = json.loads(chunk_data)
                    if chunk_json["done"]:
                        break
                    else:
                        content += chunk_json["message"]["content"]
        return content

# 定义CodeExecutor角色
class CodeExecutor:
    def execute_code(self, code, language):
        if language == "Python":
            with open("temp.py", "w") as f:
                f.write(code)
            try:
                result = subprocess.run(["python", "temp.py"], capture_output=True, text=True)
                logging.info(f"[CodeExecutor] 执行结果: {result.stdout}")
                return result.stdout
            except Exception as e:
                logging.error(f"[CodeExecutor] 执行错误: {e}")
                return str(e)
        elif language == "JavaScript":
            with open("temp.js", "w") as f:
                f.write(code)
            try:
                result = subprocess.run(["node", "temp.js"], capture_output=True, text=True)
                logging.info(f"[CodeExecutor] 执行结果: {result.stdout}")
                return result.stdout
            except Exception as e:
                logging.error(f"[CodeExecutor] 执行错误: {e}")
                return str(e)
        else:
            logging.error(f"[CodeExecutor] 不支持的语言")
            return "不支持的语言"

# 定义UserInterface角色
class UserInterface:
    def __init__(self, coordinator):
        self.coordinator = coordinator
        self.log_output = None

    def ai_code_assistant(self, instruction, language, max_attempts):
        return self.coordinator.process_instruction(instruction, language, max_attempts)

    def update_logs(self):
        while True:
            time.sleep(1)
            if self.log_output:
                self.log_output.update(value=log_stream.getvalue())

    def launch(self):
        with gr.Blocks() as iface:
            gr.Markdown("# AI代码助手")
            with gr.Row():
                with gr.Column():
                    instruction = gr.Textbox(label="指令")
                    language = gr.Dropdown(["Python", "JavaScript"], label="语言")
                    max_attempts = gr.Number(label="最大交互次数", value=5)
                    submit_btn = gr.Button("提交")
                with gr.Column():
                    with gr.Tabs():
                        with gr.TabItem("生成的代码"):
                            generated_code = gr.Textbox(label="生成的代码", lines=10)
                        with gr.TabItem("程序结构"):
                            program_structure = gr.Textbox(label="程序结构", lines=10)
                        with gr.TabItem("审查结果"):
                            review_result = gr.Textbox(label="审查结果", lines=10)
                        with gr.TabItem("执行结果"):
                            execution_result = gr.Textbox(label="执行结果", lines=10)
                        with gr.TabItem("日志输出"):
                            self.log_output = gr.Textbox(label="日志输出", lines=10)
                        with gr.TabItem("修改代码"):
                            modified_code = gr.Textbox(label="修改代码", lines=10)
                            modify_btn = gr.Button("提交修改")

            submit_btn.click(
                fn=self.ai_code_assistant,
                inputs=[instruction, language, max_attempts],
                outputs=[generated_code, program_structure, review_result, execution_result, self.log_output]
            )

            modify_btn.click(
                fn=self.coordinator.process_modified_code,
                inputs=[modified_code, language],
                outputs=[generated_code, program_structure, review_result, execution_result, self.log_output]
            )

            gr.Examples(
                examples=[
                    ["计算一个数的阶乘", "Python", 5],
                    ["对一个数字列表按升序排序", "JavaScript", 5]
                ],
                inputs=[instruction, language, max_attempts],
                outputs=[generated_code, program_structure, review_result, execution_result, self.log_output]
            )

            # 启动日志更新线程
            threading.Thread(target=self.update_logs, daemon=True).start()

        iface.launch()

# 定义Coordinator角色
class Coordinator:
    def __init__(self, requirement_analyzer, program_structure, code_generator, code_reviewer, code_executor):
        self.requirement_analyzer = requirement_analyzer
        self.program_structure = program_structure
        self.code_generator = code_generator
        self.code_reviewer = code_reviewer
        self.code_executor = code_executor
        self.interaction_count = 0
        self.max_attempts = 5
        self.current_code = None
        self.current_language = None
        self.continue_after_max_attempts = False

    def process_instruction(self, instruction, language, max_attempts):
        self.max_attempts = max_attempts
        self.current_language = language
        self.continue_after_max_attempts = False
        # 分析用户指令
        requirement = self.requirement_analyzer.analyze_instruction(instruction)
        # 确定程序结构
        structure = self.program_structure.determine_structure(requirement)
        
        # 循环生成和审查代码,直到审查通过或达到最大交互次数
        while self.interaction_count < self.max_attempts or self.continue_after_max_attempts:
            self.interaction_count += 1
            logging.info(f"[Coordinator] 第 {self.interaction_count} 次尝试")
            # 生成代码
            code = self.code_generator.generate_code(requirement, structure, language)
            self.current_code = code
            # 审查代码
            review = self.code_reviewer.review_code(code, language)
            
            # 如果审查通过,跳出循环
            if "代码已经很完善" in review:
                break
            
            # 否则,重新生成代码
            requirement = f"修复以下代码: {code}"
        
        # 如果达到最大交互次数,提示用户修改代码
        if self.interaction_count >= self.max_attempts and not self.continue_after_max_attempts:
            logging.info("[Coordinator] 已达到最大交互次数,任务结束。")
            return code, structure, review, "已达到最大交互次数,任务结束。", self.get_logs()
        
        # 执行代码
        result = self.code_executor.execute_code(code, language)
        
        # 检查执行结果,如果发现问题,驱动相关角色行动
        if "Error" in result:
            self.handle_error(code, result)
        
        return code, structure, review, result, self.get_logs()

    def handle_error(self, code, error_message):
        # 这里可以实现错误处理逻辑,例如重新生成代码或提示用户修改代码
        logging.error(f"[Coordinator] 检测到错误: {error_message}")
        logging.info("[Coordinator] 驱动相关角色采取行动...")
        # 提示用户修改代码
        self.current_code = code
        return code, "", "", f"检测到错误,请修改代码: {error_message}", self.get_logs()

    def process_modified_code(self, modified_code, language):
        self.current_code = modified_code
        self.continue_after_max_attempts = True
        # 执行修改后的代码
        result = self.code_executor.execute_code(modified_code, language)
        
        # 检查执行结果,如果发现问题,驱动相关角色行动
        if "Error" in result:
            self.handle_error(modified_code, result)
        
        return modified_code, "", "", result, self.get_logs()

    def get_logs(self):
        # 获取日志输出
        return log_stream.getvalue()

# 主程序
def main():
    # 初始化API URL和模型
    api_url = "http://localhost:11434/api/chat"
    model = "qwen2.5"

    # 创建角色实例
    requirement_analyzer = RequirementAnalyzer(api_url, model)
    program_structure = ProgramStructure(api_url, model)
    code_generator = CodeGenerator(api_url, model)
    code_reviewer = CodeReviewer(api_url, model)
    code_executor = CodeExecutor()
    coordinator = Coordinator(requirement_analyzer, program_structure, code_generator, code_reviewer, code_executor)
    user_interface = UserInterface(coordinator)

    # 启动用户界面
    user_interface.launch()

if __name__ == "__main__":
    main()

一个输出的例子(对一个数字列表按升序排序):

生成的代码:

复制代码
class NumberSorter {
    constructor(numbers) {
        this.numbers = numbers;
    }

    // 返回排序后的数组
    sortNumbers() {
        return [...this.numbers].sort((a, b) => a - b);
    }

    // 对原数组进行就地排序
    inPlaceSort() {
        this.numbers.sort((a, b) => a - b);
    }
}

// 示例用法:
const numbersList = [5, 3, 9, 1, 7];
const sorter = new NumberSorter(numbersList);

// 调用 sortNumbers 方法,返回排序后的数组
const sortedNumbers = sorter.sortNumbers();
console.log(sortedNumbers);  // 输出: [1, 3, 5, 7, 9]

// 使用 inPlaceSort 方法对原始数组进行就地排序
sorter.inPlaceSort();
console.log(numbersList);    // 输出: [1, 3, 5, 7, 9]

评审结果:

这段JavaScript代码整体结构合理、功能明确。根据代码实现的功能和逻辑,提出以下改进建议:

建议

  1. **文档注释**:在类中添加详细的注释或文档说明,例如对每个方法的输入输出进行解释。

  2. **错误处理**:增加参数验证以确保`numbers`是一个有效的数组。可以使用ES6的`Array.isArray()`来检查类型是否正确。

  3. **可读性增强**:将内部实现细节封装得更紧密,并在需要的地方添加注释,提高代码可维护性和理解度。

改进后的代码

```javascript

class NumberSorter {

/**

* 构造函数初始化一个数字排序器实例

* @param {number\[\]} numbers - 需要排序的数字数组

*/

constructor(numbers) {

if (!Array.isArray(numbers)) {

throw new Error('Numbers must be an array');

}

this.numbers = numbers;

}

/**

* 返回排序后的数组副本。

* @returns {number\[\]} 排序后的数字数组

*/

sortNumbers() {

return ...this.numbers.sort((a, b) => a - b);

}

/**

* 对原数组进行就地排序。

*/

inPlaceSort() {

this.numbers.sort((a, b) => a - b);

}

}

// 示例用法:

const numbersList = 5, 3, 9, 1, 7;

try {

const sorter = new NumberSorter(numbersList);

// 调用 sortNumbers 方法,返回排序后的数组

const sortedNumbers = sorter.sortNumbers();

console.log(sortedNumbers); // 输出: 1, 3, 5, 7, 9

// 使用 inPlaceSort 方法对原始数组进行就地排序

sorter.inPlaceSort();

console.log(numbersList); // 输出: 1, 3, 5, 7, 9

} catch (error) {

console.error(error.message);

}

```

结论

整体来说,这段代码已经很完善了。通过上述建议,我们增加了错误处理和参数检查,并且使代码更加健壮、易读。这将帮助开发人员更好地理解和维护这个类。

执行结果:

日志输出:

2024-11-04 08:59:04,259 - DEBUG - Loaded backend agg version v2.2.

2024-11-04 08:59:04,267 - DEBUG - Loaded backend MacOSX version unknown.

2024-11-04 08:59:06,212 - DEBUG - Loaded backend agg version v2.2.

2024-11-04 08:59:06,220 - DEBUG - Starting new HTTP connection (1): localhost:11434

2024-11-04 08:59:08,751 - DEBUG - http://localhost:11434 "POST /api/chat HTTP/1.1" 200 None

2024-11-04 08:59:15,931 - INFO - RequirementAnalyzer 分析的需求: 这个指令要求对给定的一组数字进行排序操作,使其按照从小到大的顺序排列。

在编程中实现这一功能通常可以使用语言内置的排序函数或方法。例如,在Python中你可以这样写:

```python

numbers = 5, 3, 9, 1, 7

sorted_numbers = sorted(numbers)

print(sorted_numbers) # 输出: 1, 3, 5, 7, 9

```

或者在Python中直接对列表进行排序,不返回新的列表:

```python

numbers = 5, 3, 9, 1, 7

numbers.sort()

print(numbers) # 输出: 1, 3, 5, 7, 9

```

这两个例子都实现了将原始数字列表按升序排序。你可以根据具体使用的编程语言选择合适的方法来实现这一操作。

2024-11-04 08:59:15,936 - DEBUG - Starting new HTTP connection (1): localhost:11434

2024-11-04 08:59:16,873 - DEBUG - http://localhost:11434 "POST /api/chat HTTP/1.1" 200 None

2024-11-04 08:59:27,923 - INFO - ProgramStructure 确定的结构: 为了模拟给定的功能,我们可以设计一个简单的类结构来封装排序功能。以下是一个可能的Python实现:

```python

class NumberSorter:

def init(self, numbers):

self.numbers = numbers

def sort_numbers(self):

return sorted(self.numbers)

def in_place_sort(self):

self.numbers.sort()

return self.numbers

示例用法:

numbers_list = 5, 3, 9, 1, 7

sorter = NumberSorter(numbers_list)

sorted_numbers = sorter.sort_numbers() # 调用 sort_numbers 方法,返回排序后的列表

print(sorted_numbers) # 输出: 1, 3, 5, 7, 9

使用 in_place_sort 方法对原始列表进行就地排序

in_place_sorted_numbers = sorter.in_place_sort()

print(in_place_sorted_numbers) # 输出: 1, 3, 5, 7, 9

```

在这个例子中,我们创建了一个名为 `NumberSorter` 的类,并为其定义了两个方法:`sort_numbers` 和 `in_place_sort`。这两个方法分别用于返回排序后的列表和对原始列表进行原地排序。

通过这样的设计,你可以轻松地在不同的场景下使用这个类来实现按升序排列的功能。

2024-11-04 08:59:27,923 - INFO - Coordinator 第 2 次尝试

2024-11-04 08:59:27,926 - DEBUG - Starting new HTTP connection (1): localhost:11434

2024-11-04 08:59:30,027 - DEBUG - http://localhost:11434 "POST /api/chat HTTP/1.1" 200 None

2024-11-04 08:59:48,335 - INFO - CodeGenerator 生成的代码: class NumberSorter {

constructor(numbers) {

this.numbers = numbers;

}

// 返回排序后的数组

sortNumbers() {

return ...this.numbers.sort((a, b) => a - b);

}

// 对原数组进行就地排序

inPlaceSort() {

this.numbers.sort((a, b) => a - b);

}

}

// 示例用法:

const numbersList = 5, 3, 9, 1, 7;

const sorter = new NumberSorter(numbersList);

// 调用 sortNumbers 方法,返回排序后的数组

const sortedNumbers = sorter.sortNumbers();

console.log(sortedNumbers); // 输出: 1, 3, 5, 7, 9

// 使用 inPlaceSort 方法对原始数组进行就地排序

sorter.inPlaceSort();

console.log(numbersList); // 输出: 1, 3, 5, 7, 9

2024-11-04 08:59:48,338 - DEBUG - Starting new HTTP connection (1): localhost:11434

2024-11-04 08:59:49,386 - DEBUG - http://localhost:11434 "POST /api/chat HTTP/1.1" 200 None

2024-11-04 09:00:09,293 - INFO - CodeReviewer 代码审查: 这段JavaScript代码整体结构合理、功能明确。根据代码实现的功能和逻辑,提出以下改进建议:

建议

  1. **文档注释**:在类中添加详细的注释或文档说明,例如对每个方法的输入输出进行解释。

  2. **错误处理**:增加参数验证以确保`numbers`是一个有效的数组。可以使用ES6的`Array.isArray()`来检查类型是否正确。

  3. **可读性增强**:将内部实现细节封装得更紧密,并在需要的地方添加注释,提高代码可维护性和理解度。

改进后的代码

```javascript

class NumberSorter {

/**

* 构造函数初始化一个数字排序器实例

* @param {number\[\]} numbers - 需要排序的数字数组

*/

constructor(numbers) {

if (!Array.isArray(numbers)) {

throw new Error('Numbers must be an array');

}

this.numbers = numbers;

}

/**

* 返回排序后的数组副本。

* @returns {number\[\]} 排序后的数字数组

*/

sortNumbers() {

return ...this.numbers.sort((a, b) => a - b);

}

/**

* 对原数组进行就地排序。

*/

inPlaceSort() {

this.numbers.sort((a, b) => a - b);

}

}

// 示例用法:

const numbersList = 5, 3, 9, 1, 7;

try {

const sorter = new NumberSorter(numbersList);

// 调用 sortNumbers 方法,返回排序后的数组

const sortedNumbers = sorter.sortNumbers();

console.log(sortedNumbers); // 输出: 1, 3, 5, 7, 9

// 使用 inPlaceSort 方法对原始数组进行就地排序

sorter.inPlaceSort();

console.log(numbersList); // 输出: 1, 3, 5, 7, 9

} catch (error) {

console.error(error.message);

}

```

结论

整体来说,这段代码已经很完善了。通过上述建议,我们增加了错误处理和参数检查,并且使代码更加健壮、易读。这将帮助开发人员更好地理解和维护这个类。

2024-11-04 09:00:09,453 - INFO - CodeExecutor 执行结果: 1, 3, 5, 7, 9

1, 3, 5, 7, 9

以上例子比较简单,主要是为了演示整个交互的过程,当遇到复杂任务时,可能就比较难以实现了,当前还没有尝试,比如:开发一个产品或做一个项目。根据直接使用AI编程的经历,大概率会失败,根因为需要进行无数次的迭代,有时候迭代未必会成功,但这个是一个趋势,随时LLM模型的增强,必然会让越来越多的复杂任务得以实现,当前可以专注于现简单、容易实现的应用场景。

相关推荐
IT_陈寒23 分钟前
SpringBoot自动配置差点让我加班到凌晨
前端·人工智能·后端
yumgpkpm36 分钟前
Acceldata ODP(Open Data Platform)3.3.6.4(RHEL9)保姆级完整安装手册
大数据·人工智能·hive·hadoop·kafka·hbase·cloudera
程序员cxuan38 分钟前
腾讯又来一王炸,开源版 WorkBuddy 太夯了!
人工智能·后端·程序员
邓工说电1 小时前
智慧断路器安全吗?数据加密、离线保护与合规认证全解读
大数据·数据库·人工智能·智能断路器·炜晔科技
阿里云大数据AI技术1 小时前
Lance 数据检索怎么选,当然阿里云 Milvus 向量湖
人工智能
出海客1 小时前
跨境电商多语言客服知识库怎么建:资料结构、检索边界与人工升级
大数据·人工智能
xsd202411181 小时前
从自主导航到视觉读表:一台工业巡检机器人的全栈技术链路拆解
人工智能
袁哥大话安全1 小时前
巡隐WEBSHELL扫描软件
人工智能·安全·web
论文复现现场1 小时前
8卡4090能跑70B吗?Llama-2显存预算、QLoRA与通信瓶颈
人工智能·深度学习·分布式训练·llama·显存·qlora·算家云
酷虎软件1 小时前
如何用AI创作AI歌曲AI音乐
人工智能