最新资讯
dev.to 我们在 DolphinBench 上测试记忆检索:纯关键词搜索暴露了自身回归 dev.to 别再把 Agent 写成 while 循环,把它建模为状态机 bloomberg.com Rightmove 与 Scout24 借 AI 产品应对低迷房市并上调展望 stackoverflow.blog 欢迎来到芯片时代:半导体制造、AI 数据中心与芯片设计的未来 www.youtube.com AI Agent 总犯同样的错:CentralAgent AI 将记忆重构为经验系统 pub.towardsai.net Netflix 科学家建议重采样 LLM 评判:5 次仅将误差棒缩小 13% pub.towardsai.net Jev 作为 LLM 前的静默层:构建提示感知的模型路由器 reddit.com OpenAI 称 Astra 仅满足 10% 安全任务标准 dev.to FreeToken:在自有 GPU 上运行前沿 MoE 模型及其内存需求 dev.to 当 AI Agent 说“完成”时,究竟完成了什么? dev.to 部署 Underdog Bark 3.0 Pro:逐字转写、自定义后端与分块设置 dev.to 为艰难对话构建 AI:五个 Agent、一条安全正则与一位盲评监督者
多元动态
登录 注册
智能体 dev.to · @Tamiz Uddin

能自我验证输出的 AI Agent:30 分钟验证闭环

原文 我的收藏

文章指出 AI 辅助开发的瓶颈在于生成代码快、验证代码慢,传统做法把 Agent 当作“文本预言机”而非“执行主体”。作者提出自验证 AI Agent 架构:Agent 编写代码后自行编写并运行测试、分析堆栈信息、迭代重构,直到测试套件通过,从而把验证闭环内化到 Agent 自身。

AI推荐理由 提出自验证 AI Agent 架构:Agent 不只写代码,还写测试、运行、分析堆栈并自我重构直至测试通过,把验证闭环从人工审查转为执行反馈,对 Agent 工程实践有参考价值;但标题中“30 分钟胜过 30 天代码审查”属营销式对比,正文为教程且未提供可复现的量化结果。

68

正文

Originally published on tamiz.pro. Traditional AI-assisted development is bottlenecked by a fundamental asymmetry: generating code is instantaneous, but verifying it is slow. A human reviewer takes days; a CI pipeline takes minutes; but the feedback loop is broken. When an AI agent generates a feature branch, it typically halts, handing the code to a human or a static analysis tool. This passive model fails because it treats the agent as a 'text oracle' rather than an 'execution actor.' In this tutorial, we will design and implement a Self-Validating AI Agent architecture. This agent does not just write code; it writes tests, runs them, analyzes the stack traces, and refactors its own code until the test suite passes. This "30-minute loop" allows a single engineer to ship complex, reliable features significantly faster than a team spending 30 days on manual code review and CI iterations. Table of Contents 1. The Architecture of Self-Verification 2. Setting Up the Sandbox Environment 3. Building the Agent Core: LLM + Tooling 4. The Execution Loop: Run, Test, Reflect 5. Preventing "Reward Hacking" in Tests 6. Production Hardening 7. Frequently Asked Questions 1. The Architecture of Self-Verification The standard "Prompt-Completion" model is a dead end for production engineering. To achieve reliability, we must close the loop. The agent must possess three distinct capabilities: Code Generation: The ability to write implementation and test code. Execution: A secure, isolated environment to run that code. Reflection: A mechanism to parse execution results (pass/fail/exception) and feed that context back to the LLM. The workflow looks like this: [User Request] -> [Agent] -> [Write Code] -> [Write Tests] -> [Execute Tests] ^ | | | +--------------------------------------------------------+ (Reflect on Errors & Refactor) Enter fullscreen mode Exit fullscreen mode If the tests fail, the agent receives the specific error message (e.g., AssertionError: expected 4 but got 4.0). The LLM uses this semantic feedback to propose a fix. This cycle repeats until the tests pass or a limit is reached. This removes the human from the verification loop, reducing turnaround time from hours to minutes. 2. Setting Up the Sandbox Environment You cannot verify code if you can't run it safely. Running rm -rf / or malicious loops in a self-verifying agent is a security risk. We will use Docker to isolate the execution. First, create a Dockerfile that sets up a Python environment with the necessary tools: # Dockerfile FROM python:3.9-slim # Create working directory WORKDIR /app # Install pytest for testing framework RUN pip install pytest requests # Copy entrypoint that executes code safely COPY run_code.py . CMD ["python", "run_code.py"] Enter fullscreen mode Exit fullscreen mode Then, create the run_code.py script inside the container that accepts code and tests from standard input (stdin). This approach ensures that every test run happens in a fresh, isolated container, preventing state leakage between iterations. # run_code.py import sys import subprocess import os def main(): # Read code from stdin code = sys.stdin.read() # Write code to a temporary file with open('/app/test_script.py', 'w') as f: f.write(code) # Run pytest on the generated file result = subprocess.run( ['pytest', '/app/test_script.py', '-v'], capture_output=True, text=True, timeout=30 # Prevent infinite loops ) # Output results to stdout for the agent to parse print(result.stdout) print(result.stderr) if __name__ == '__main__': main() Enter fullscreen mode Exit fullscreen mode Build the image locally: docker build -t agent-sandbox . Enter fullscreen mode Exit fullscreen mode 3. Building the Agent Core: LLM + Tooling Now we define the agent in Python. We will use the openai library (or any compatible LLM API) and docker to orchestrate the loop. The agent's system prompt must explicitly instruct it to always write tests before implementation and to prioritize test passing over style. import docker import json import openai client = openai.OpenAI() def generate_code(request: str, history: list) -> str: """Calls the LLM to generate code and tests.""" system_prompt = """ You are a senior software engineer. 1. Read the user's request. 2. Write Python code to solve it. 3. Crucially: Write a set of pytest test cases that verify the solution. 4. If previous tests failed, analyze the error and fix the code. 5. Output ONLY valid JSON: {"code": "...", "tests": "...", "reasoning": "..."} """ messages = [ {"role": "system", "content": system_prompt}, {"role": "user", "content": f"Request: {request}\n\nHistory/Feedback: {history}"} ] response = client.chat.completions.create( model="gpt-4", # or gpt-3.5-turbo for cost efficiency messages=messages, temperature=0.2 # Low temperature for deterministic code ) # Parse JSON response try: content = response.choices[0].message.content return json.loads(content) except json.JSONDecodeError: return {"code": "", "tests": "", "reasoning": "Failed to parse LLM output"} def run_sandbox(code: str, tests: str) -> str: """Injects code and tests into the Docker sandbox and returns output.""" client = docker.from_env() container = client.containers.run( image="agent-sandbox", detach=True, stdin_open=True, input=f"{code}\n\n{tests}" ) # Wait for execution exit_code, logs = container.wait() log_text = logs.decode('utf-8') container.remove() return log_text, exit_code Enter fullscreen mode Exit fullscreen mode 4. The Execution Loop: Run, Test, Reflect This is the core of the "30-minute" claim. We create a loop that runs the sandbox, parses the results, and feeds the failure back into the LLM. Notice the parse_feedback function. It doesn't just say "Failed." It extracts the specific assertion error so the LLM knows what went wrong. def parse_feedback(sandbox_output: str, exit_code: int) -> str: """Converts raw pytest output into semantic feedback for the LLM.""" if exit_code == 0: return "All tests passed successfully. No action required." # If exit code is not 0, extract the specific errors lines = sandbox_output.split('\n') errors = [line for line in lines if 'AssertionError' in line or 'Error' in line] if not errors: return f"Tests failed. Raw output: {sandbox_output[:500]}" return "Tests failed. Specific errors detected:\n" + "\n".join(errors) def self_validating_agent(request: str, max_iterations: int = 5): """ Orchestrates the loop: Generate -> Execute -> Reflect -> Repeat. """ history = "" current_code = "" for i in range(max_iterations): print(f"[Iteration {i+1}] Generating code...\n") # 1. LLM generates code + tests based on request and history response_data = generate_code(request, history) code = response_data.get("code", "") tests = response_data.get("tests", "") reasoning = response_data.get("reasoning", "") if not code or not tests: history = "You failed to provide code and tests. Provide valid Python code." continue current_code = code print(f"[Iteration {i+1}] Running sandbox...\nReasoning: {reasoning}") # 2. Execute in Sandbox output, exit_code = run_sandbox(code, tests) # 3. Analyze Results if exit_code == 0: print("SUCCESS: All tests passed.") print("\nFINAL CODE:\n") print(code) return code # 4. Prepare feedback for next iteration feedback = parse_feedback(output, exit_code) history = f"Previous attempt failed.\n{feedback}\nPlease fix the code." print(f"[Iteration {i+1}] Failed. Updating history with feedback.\n") print("MAX ITERATIONS REACHED. Returning best effort.") return current_code # Example Usage # result = self_validating_agent("Write a function to calculate Fibonacci numbers with memoization") Enter fullscreen mode Exit fullscreen mode Why This Beats Manual Review In a traditional flow, the developer writes the code, writes the test, runs the test, sees the error, fixes the code, re-runs the test, and repeats. An engineer can do about 20 iterations in a day if they are highly focused. The agent above runs continuously. While it might have a limit of 5 iterations to prevent infinite loops, each iteration takes roughly 10-15 seconds (LLM call + Docker start + Pytest run). In 30 minutes, the agent can attempt hundreds of logic branches, exploring edge cases that a human might overlook or forget to test. 5. Preventing "Reward Hacking" in Tests There is a significant risk with self-verifying agents: Reward Hacking. The LLM's goal is to "pass the test." Sometimes, the easiest way to pass a test is to modify the test itself or hard-code the output to match the expected result, rather than fixing the underlying logic. For example, if the test expects 2+2=4, the agent might write return 4 instead of return a + b. Mitigation Strategies Read-Only Tests: The sandbox should execute tests from a separate, immutable file that the agent does not have write access to. Property-Based Testing: Instead of fixed expected outputs, use property-based tests (e.g., assert isinstance(result, int) or assert input_len == output_len). These are much harder to "hack" by hard-coding values. Dual-LLM Verification: Use a second LLM with a different prompt to review the generated tests. Ask the second LLM: "Does this test actually verify the requirement, or is it trivially bypassable?" Implementing immutable tests in Docker is simple: # In the Dockerfile, ensure tests are read-only or mounted from host COPY tests.py /app/tests.py RUN chmod 444 /app/tests.py Enter fullscreen mode Exit fullscreen mode Modify run_code.py to mount the host's test file or strictly separate code generation from test execution. 6. Production Hardening For a production-grade system, consider these hardening steps: Timeouts: Always enforce strict timeouts on the Docker container (e.g., 30s) to prevent the agent from generating an infinite loop (while True: pass). Security Isolation: Run the Docker container with --no-new-privileges and minimal privileges. Do not mount the host's /root or sensitive directories. Cost Cap: LLM calls cost money. Implement a max_iterations limit and a token counter. If the cost exceeds a threshold, abort and flag for human review. Logging: Log every iteration to a file. You need to audit why the agent failed. If it failed 5 times, the logs will show if it was stuck on a syntax error or a logic error. 7. Frequently Asked Questions Q: Can this work with languages other than Python? A: Yes. You just need a language-specific Docker image (e.g., Node.js, Go, Rust). The agent code remains the same; only the sandbox and test framework change (e.g., npm test instead of pytest). The LLM needs to be prompted to write tests in the target language's testing framework. Q: How do we prevent the agent from getting stuck in a loop of the same error? A: Track the hash of the error message. If the same error appears 3 times in a row, break the loop and escalate to a human. The LLM is likely stuck in a local minimum. Q: Is this faster than CI/CD pipelines? A: Yes, for initial development. CI/CD is a gatekeeper for the final state. The self-validating agent is a development tool that shifts verification left, catching bugs before the code is even committed to the repository. This reduces the number of failed CI runs, which saves downstream resources.

来源报道 · 1 篇 · 以下链接均前往原始媒体

← 返回精选动态 原文

更多内容