最近在尝试各种 AI 代码助手时发现了一个非常有趣的现象以“全能”和“复杂”著称的 Claude Code其核心的代码生成与理解能力似乎可以被一个极其精简的架构所模拟。这个架构就是Pi Agent。它没有庞大的参数不依赖复杂的云端推理仅用300个token的上下文和4个核心工具就实现了令人惊讶的代码辅助效果。这引发了我的思考我们是否过度设计了AI Agent对于开发者日常的代码补全、解释和重构需求一个“反向极简”的方案是否更高效本文将为你完整拆解这个“极简主义”的 Pi Agent 实现思路。我们将从零开始构建一个能理解需求、调用工具、生成代码的微型智能体。无论你是想深入理解 AI Agent 的工作机制还是希望为自己的项目嵌入一个轻量级、可掌控的代码助手这篇文章都将提供一套完整的、可运行的实战方案。你将掌握如何用有限的上下文和精准的工具定义让大语言模型LLM发挥出超越其本身规模的“执行力”。1. 背景与核心概念为什么需要“极简”Agent在讨论 Pi Agent 之前我们首先要厘清几个关键概念AI Agent、Claude Code以及Token在本次讨论中的特殊含义。AI Agent智能体通常指能够感知环境、进行决策并执行行动以实现目标的程序。在代码生成领域一个 Agent 可以理解开发者的自然语言需求如“写一个快速排序函数”然后规划步骤分析需求、选择算法、编写代码、测试最后通过调用代码编辑器、终端、浏览器等“工具”来执行并交付结果。传统的强大 Agent如 Claude Code 背后的系统往往集成了数十甚至上百个工具并拥有超长的上下文窗口如 200K tokens以处理极其复杂的任务。Claude Code是 Anthropic 公司推出的专注于代码的 AI 助手它深度集成在 IDE 中能够进行代码补全、生成、解释、调试和重构。其强大之处在于对代码上下文的理解极其深入并能执行复杂的多步操作。然而这种强大也伴随着一定的“黑盒”性和资源消耗。Token是 LLM 处理文本的基本单位。在本文的“反向极简”语境下300 tokens并非指模型的总上下文长度而是我们设计给 Agent 的核心“系统提示词System Prompt”和“工具描述”的精心裁剪后的长度。这个限制逼迫我们剔除所有冗余描述只保留最本质的指令和能力定义从而实现指令的极致精确和模型响应的快速稳定。那么为什么需要“极简”Agent成本与速度更短的提示词意味着更低的 API 调用成本按 token 计费和更快的响应速度。可控与可解释性工具越少逻辑越清晰越容易调试和优化。你不会被一个拥有 50 个工具的 Agent 的不可预测行为所困扰。专注与高效对于 80% 的日常开发任务写函数、修 Bug、写注释我们真的需要动用“核武器”吗一个专注的“瑞士军刀”可能更顺手。学习与定制极简的实现是理解 Agent 原理的最佳教材。你可以轻松地基于它添加自己的工具构建专属助手。Pi Agent 的设计哲学就是用最小的、必要的复杂性解决最核心的问题。接下来我们就开始动手实现它。2. 环境准备与版本说明我们将使用 Python 作为实现语言因为它拥有丰富的 AI 生态库和简洁的语法。本示例将基于 OpenAI 兼容的 API例如 OpenAI GPT-3.5/4或国内可访问的 DeepSeek、通义千问等进行构建。你可以轻松替换为任何支持 Function Calling 或 Tool Calling 的模型。核心环境操作系统Windows 10/11, macOS, 或 Linux (Ubuntu 20.04)。本文命令以 Linux/macOS 为例。Python 版本 3.8包管理工具pip主要依赖库openai用于调用大语言模型 API。我们将使用其最新的支持 Tool Calling 的客户端。python-dotenv用于管理环境变量安全地存储 API 密钥。版本说明本文代码基于openai1.0.0版本编写该版本引入了全新的客户端架构和 Tool Calling 接口。请务必注意与你现有项目的兼容性。初始化项目# 1. 创建项目目录并进入 mkdir pi_agent_demo cd pi_agent_demo # 2. 创建虚拟环境推荐 python -m venv venv # 3. 激活虚拟环境 # Linux/macOS: source venv/bin/activate # Windows: # venv\Scripts\activate # 4. 安装核心依赖 pip install openai python-dotenv # 5. 创建必要的文件 touch main.py tools.py utils.py .env项目结构预览pi_agent_demo/ ├── .env # 存储环境变量如 API_KEY ├── main.py # Agent 主循环和逻辑 ├── tools.py # 4个核心工具的实现 └── utils.py # 辅助函数如裁剪上下文3. 核心原理Agent、Tool Calling 与极简提示词在编写代码前我们需要理解三个核心机制。3.1 AI Agent 的基本运行循环一个典型的基于 LLM 的 Agent 遵循“思考-行动-观察”的循环思考LLM 根据用户输入和当前上下文决定下一步该做什么。是直接回答还是调用某个工具行动如果决定调用工具LLM 会以严格的 JSON 格式输出工具调用请求包含工具名和参数。观察程序执行指定的工具并将工具执行的结果成功或失败返回给 LLM。再思考LLM 结合工具执行结果生成下一步的响应或行动。循环直至任务完成。3.2 Tool Calling工具调用这是让 LLM 从“聊天”变为“执行”的关键。OpenAI 等 API 提供了原生支持。你需要在请求中以列表形式向 LLM 描述可用的工具函数。LLM 在需要时会在响应中返回一个特殊的tool_calls字段。你的程序解析这个字段找到对应的本地函数并执行。将执行结果作为新的消息附加到对话历史中再次请求 LLM让它基于结果进行总结或下一步行动。3.3 300 Token 的极简系统提示词设计这是 Pi Agent 的“灵魂”。我们的目标是让 LLM 牢牢记住自己的身份和核心职责不产生任何歧义或多余行为。# 这是一个概念示例实际会放在 main.py 的 system_message 中 SYSTEM_PROMPT 你是一个高效、精准的编程助手Pi。你只专注于代码相关任务并严格使用以下工具 1. write_code: 根据要求编写代码片段。必须指定语言。 2. explain_code: 解释提供的代码的功能和逻辑。 3. refactor_code: 重构代码以提高可读性或性能。 4. search_web: 当需要最新知识如库API时进行网络搜索。 规则 - 用户问题可能是模糊的你必须先澄清再行动。 - 一次只使用一个工具除非用户明确要求多步。 - 工具结果会提供给你基于结果直接给出最终答案。 - 回答务必简洁、专业直接解决编程问题。 - 如果工具无法满足需求直接说明并给出建议。 经过精心打磨上述提示词可以控制在 300 tokens 以内英文更少。它明确了身份、工具、规则和边界没有一句废话。4. 完整实战构建四工具 Pi Agent现在让我们开始编码。我们将实现上述四个工具和一个简单的 Agent 循环。4.1 配置环境变量首先在.env文件中安全地配置你的 API 密钥和基础 URL如果你使用非 OpenAI 官方服务。# .env OPENAI_API_KEYsk-your-actual-api-key-here # 如果使用第三方兼容服务例如 DeepSeek OPENAI_BASE_URLhttps://api.deepseek.com MODEL_NAMEdeepseek-chat # 或 gpt-3.5-turbo, gpt-4-turbo-preview 等重要请勿将.env文件提交到版本控制系统如 Git。确保它在.gitignore中。4.2 实现四个核心工具在tools.py中我们定义工具函数。注意这些函数本身并不复杂它们代表了 Agent 的“手和脚”。# tools.py import subprocess import sys import json # 注意search_web 需要额外安装 requests 库我们稍后处理 def write_code(language: str, task: str) - str: 根据任务描述编写代码。 Args: language: 编程语言如 python, javascript, java。 task: 具体的编码任务描述。 Returns: 生成的代码字符串。 # 在实际高级Agent中这里会调用LLM。但为了极简和演示 # 我们假设这是一个“存根”stub真正的代码生成由主Agent的LLM完成。 # 这个工具的存在是给LLM一个“行动出口”。 return f[代码生成工具被调用]\n语言{language}\n任务{task}\n[提示我将为您构思{language}代码。] def explain_code(code: str) - str: 解释提供的代码。 Args: code: 需要解释的代码字符串。 Returns: 对代码功能、逻辑的解释。 # 同样这是一个存根。主LLM会处理解释逻辑。 return f[代码解释工具被调用]\n待解释代码\n\n{code[:200]}...\n\n[提示我将分析这段代码。] def refactor_code(code: str, goal: str 提高可读性) - str: 重构代码以达到特定目标。 Args: code: 需要重构的原始代码。 goal: 重构目标如‘提高可读性’、‘优化性能’、‘符合PEP8’。 Returns: 重构后的代码。 return f[代码重构工具被调用]\n重构目标{goal}\n原始代码\n\n{code[:200]}...\n\n[提示我将根据‘{goal}’进行重构。] def search_web(query: str) - str: 搜索网络获取信息例如最新的库文档、错误解决方案。 注意这是一个模拟函数。在生产环境中你需要集成真正的搜索API如Serper、Google Custom Search并处理网络错误和速率限制。 Args: query: 搜索查询词。 Returns: 搜索结果的摘要。 # 模拟搜索行为 print(f[模拟] 正在搜索: {query}, filesys.stderr) # 在实际实现中这里会是 requests.get(...) 调用 return f[网络搜索工具被调用]\n查询‘{query}’\n[模拟结果] 关于‘{query}’的最新信息可在官方文档中找到。建议使用 requests 库进行 HTTP 请求。4.3 构建 Agent 主循环这是最核心的部分在main.py中实现。# main.py import os import json from openai import OpenAI from dotenv import load_dotenv from tools import write_code, explain_code, refactor_code, search_web # 加载环境变量 load_dotenv() # 初始化 OpenAI 兼容客户端 client OpenAI( api_keyos.getenv(OPENAI_API_KEY), base_urlos.getenv(OPENAI_BASE_URL, https://api.openai.com/v1) # 默认使用 OpenAI ) MODEL os.getenv(MODEL_NAME, gpt-3.5-turbo) # 默认模型 # ---- 1. 定义极简系统提示词 (约250 tokens) ---- SYSTEM_MESSAGE { role: system, content: 你是Pi一个极简高效的编程助手。你只做四件事 1. 写代码用write_code。 2. 解释代码用explain_code。 3. 重构代码用refactor_code。 4. 搜索网络信息用search_web。 规则 - 用户问题不清晰时先问清楚。 - 一次只用一个工具除非用户要求多步。 - 基于工具结果直接给出最终答案。 - 回答要简短、精准、专业。 - 做不到就直接说。 } # ---- 2. 定义工具列表供LLM识别 ---- # 工具描述必须清晰、结构化这是LLM理解工具用途的关键。 tools [ { type: function, function: { name: write_code, description: 根据任务描述编写代码片段。必须指定编程语言。, parameters: { type: object, properties: { language: {type: string, description: 编程语言如python, javascript。}, task: {type: string, description: 具体的编码任务描述。} }, required: [language, task] } } }, { type: function, function: { name: explain_code, description: 解释一段代码的功能、逻辑和关键点。, parameters: { type: object, properties: { code: {type: string, description: 需要解释的代码字符串。} }, required: [code] } } }, { type: function, function: { name: refactor_code, description: 重构代码以改进可读性、性能或风格。, parameters: { type: object, properties: { code: {type: string, description: 需要重构的原始代码。}, goal: {type: string, description: 重构目标默认是提高可读性。, default: 提高可读性} }, required: [code] } } }, { type: function, function: { name: search_web, description: 搜索网络获取最新信息例如库的API用法、错误代码解释。, parameters: { type: object, properties: { query: {type: string, description: 搜索查询关键词。} }, required: [query] } } } ] # ---- 3. 工具名称到实际函数的映射 ---- tool_functions { write_code: write_code, explain_code: explain_code, refactor_code: refactor_code, search_web: search_web, } # ---- 4. Agent 运行函数 ---- def run_agent(user_input: str, conversation_history: list None) - tuple: 运行一次Agent循环。 Args: user_input: 用户本次输入。 conversation_history: 之前的对话消息列表。 Returns: (final_response, updated_history) if conversation_history is None: messages [SYSTEM_MESSAGE] else: messages conversation_history.copy() # 添加用户输入 messages.append({role: user, content: user_input}) # 第一次调用LLM它可能选择直接回答或调用工具 response client.chat.completions.create( modelMODEL, messagesmessages, toolstools, tool_choiceauto, # 让模型自行决定是否调用工具 ) response_message response.choices[0].message messages.append(response_message) # 将LLM的响应加入历史 # ---- 检查是否调用了工具 ---- tool_calls response_message.tool_calls if tool_calls: print(f[Agent] 决定调用 {len(tool_calls)} 个工具。) # 处理每个工具调用 for tool_call in tool_calls: function_name tool_call.function.name function_args json.loads(tool_call.function.arguments) # 找到对应的本地函数并执行 if function_name in tool_functions: function_to_call tool_functions[function_name] try: # 动态调用函数传入解析出的参数 tool_response function_to_call(**function_args) except Exception as e: tool_response f工具执行出错: {e} # 将工具执行结果作为一条新消息追加给LLM messages.append({ role: tool, tool_call_id: tool_call.id, content: str(tool_response), # 结果必须是字符串 name: function_name }) else: # 如果工具不存在返回错误信息 messages.append({ role: tool, tool_call_id: tool_call.id, content: f错误未知工具 {function_name}。, name: function_name }) # ---- 第二次调用LLM让它基于工具结果生成最终回答 ---- second_response client.chat.completions.create( modelMODEL, messagesmessages, ) final_message second_response.choices[0].message messages.append(final_message) final_response final_message.content else: # 如果LLM没有调用工具直接使用它的回复 final_response response_message.content return final_response, messages # ---- 5. 简单的命令行交互界面 ---- def main(): print(Pi Agent 已启动。输入‘退出’或‘quit’结束。) print(- * 40) history [SYSTEM_MESSAGE] # 初始化历史包含系统消息 while True: try: user_input input(\n你: ) if user_input.lower() in [退出, quit, exit]: print(Pi Agent 已关闭。) break if not user_input.strip(): continue response, history run_agent(user_input, history) print(f\nPi: {response}) except KeyboardInterrupt: print(\n\n程序被中断。) break except Exception as e: print(f\n[错误] 发生异常: {e}) # 可以选择清空历史或保留这里选择保留但打印错误 history.append({role: user, content: f[系统] 上次请求出错: {e}}) if __name__ __main__: main()4.4 运行与验证现在让我们运行这个 Agent 并与之对话。确保你的.env文件已正确配置 API 密钥。在终端运行python main.py尝试以下对话观察 Agent 如何决策和调用工具你:用 Python 写一个函数计算斐波那契数列的第 n 项。Pi:[代码生成工具被调用]... [然后 LLM 基于这个“工具调用”的上下文生成具体的 Python 代码]。最终你会看到类似def fib(n): ...的代码输出。你:解释一下这段代码def fib(n): if n 1: return n else: return fib(n-1) fib(n-2)Pi:[代码解释工具被调用]... 然后 LLM 会给出递归逻辑、时间复杂度等的解释。你:帮我重构这段代码让它更快。Pi:[代码重构工具被调用]... 目标优化性能。LLM 可能会建议使用迭代法或缓存记忆化。你:Python 里最新的异步HTTP客户端库是什么Pi:[网络搜索工具被调用]... 查询“Python latest async HTTP client library”。LLM 会基于模拟结果或真实搜索API的结果告诉你可能是httpx或aiohttp并简述特点。4.5 结果说明通过这个简单的实现你已经创建了一个具备基本“思考-行动”能力的 AI Agent。它的特点是极简提示词系统指令精炼约束了 Agent 的行为边界。精准工具调用LLM 能够根据你的问题准确选择write_code,explain_code,refactor_code,search_web中的一个。可扩展框架tools.py和tool_functions字典使得添加新工具如run_test,format_code变得非常容易。这个 Agent 虽然“小”但完整演示了 Claude Code 等复杂系统最核心的交互逻辑。区别在于Claude Code 的工具箱更庞大上下文管理更复杂并且与 IDE 深度集成。5. 常见问题与排查思路在实现和运行此类 Agent 时你可能会遇到以下问题问题现象常见原因解决思路错误ModuleNotFoundError: No module named openai未正确安装openai库或安装的版本过低。1. 确认虚拟环境已激活。2. 运行pip install --upgrade openai。3. 检查openai.__version__确保 1.0.0。错误AuthenticationError或Invalid API KeyAPI 密钥错误、未设置或环境变量未加载。1. 检查.env文件中的OPENAI_API_KEY是否正确无误。2. 确保load_dotenv()在初始化OpenAI()之前被调用。3. 如果是第三方服务确认OPENAI_BASE_URL正确。Agent 不调用工具总是直接回答1. 系统提示词不够强制。2. 工具描述不清晰。3. 模型能力不足如某些小模型对 Tool Calling 支持不好。1. 强化系统提示词例如“你必须使用工具来解决问题”。2. 检查tools列表中的description和parameters是否清晰、完整。3. 尝试更换模型如gpt-3.5-turbo或gpt-4-turbo。工具调用参数解析错误LLM 生成的参数 JSON 不符合函数定义。1. 在tool_functions调用处添加更详细的异常捕获和日志。2. 在工具描述中更严格地定义参数类型和示例。3. 使用json.loads时用try-except包裹。对话历史过长导致 API 错误或响应慢未管理上下文长度历史消息不断累积。1. 实现上下文窗口管理。只保留最近 N 轮对话或裁剪至一定 token 数。2. 在utils.py中编写一个trim_conversation函数定期清理早期消息但保留系统消息和关键工具调用。search_web工具返回模拟结果该工具是存根实现未集成真实搜索 API。1. 注册 Serper Dev、Google Custom Search 等服务的 API。2. 安装requests库pip install requests。3. 重写search_web函数发起真实的 HTTP 请求并解析返回的 JSON。注意务必处理网络超时、速率限制和错误码。上下文管理示例 (utils.py):# utils.py import tiktoken # 需要安装pip install tiktoken def count_tokens(text: str, model: str gpt-3.5-turbo) - int: 估算文本的token数量。 try: encoding tiktoken.encoding_for_model(model) except KeyError: encoding tiktoken.get_encoding(cl100k_base) return len(encoding.encode(text)) def trim_conversation(messages: list, max_tokens: int 4000, system_message: dict None) - list: 裁剪对话历史使其token总数不超过max_tokens。 优先保留系统消息和最近的消息。 if system_message is None: system_message {role: system, content: You are a helpful assistant.} # 总是保留系统消息 trimmed [system_message] current_tokens count_tokens(system_message[content]) # 从后往前添加消息保留最近的 for msg in reversed(messages): if msg[role] system: continue # 系统消息已单独处理 msg_tokens count_tokens(str(msg.get(content, ))) if current_tokens msg_tokens max_tokens: break trimmed.insert(1, msg) # 插入到系统消息之后 current_tokens msg_tokens return trimmed在主循环中可以在每次run_agent调用前或后对history应用trim_conversation。6. 最佳实践与工程建议将 Pi Agent 从演示项目变为可用的工程组件需要考虑以下几点6.1 提示词工程角色与边界系统提示词是 Agent 的“宪法”。务必清晰定义其角色、能力和限制。例如明确禁止它执行删除文件、访问非授权数据库等危险操作。少样本学习在系统消息或初始历史中提供一两个user-tool_call-assistant的完美示例能显著提升工具调用的准确率。迭代优化根据 Agent 的失败案例持续微调提示词和工具描述。这是一个实验过程。6.2 工具设计单一职责每个工具应只做一件事并且做好。write_code就只负责生成代码不要让它同时去运行测试。健壮性工具函数内部必须有完善的错误处理try-except并返回对 LLM 友好的错误信息例如“文件未找到xxx”而不是 Python 的完整 traceback。安全性这是重中之重。任何涉及文件系统、网络请求、数据库查询、系统命令的工具都必须进行严格的输入验证和权限控制。绝对不要允许 LLM 直接执行os.system(user_input)这样的操作。6.3 架构与性能状态管理对于复杂的多轮对话需要将会话状态历史、变量持久化例如存储到数据库或文件中并用一个session_id来关联。异步处理如果工具调用涉及网络 I/O如搜索、调用外部 API应使用异步编程asyncio来避免阻塞主线程提高并发能力。缓存对于频繁且结果不变的查询如解释某个常见算法可以引入缓存机制减少不必要的 LLM 调用和工具执行节省成本和时间。6.4 生产环境注意事项API 密钥管理永远不要将 API 密钥硬编码在代码中。使用.env文件、环境变量或专业的密钥管理服务如 AWS Secrets Manager, HashiCorp Vault。速率限制与重试为 LLM API 调用和工具调用如搜索 API实现指数退避的重试逻辑并遵守服务的速率限制。日志与监控记录所有的用户输入、LLM 响应、工具调用和结果。这对于调试、分析 Agent 行为和改进系统至关重要。用户输入净化对用户输入进行基本的检查和过滤防止提示词注入攻击Prompt Injection即用户输入可能篡改系统指令。通过遵循这些最佳实践你的 Pi Agent 就能从一个脆弱的脚本进化成一个健壮、可维护的 AI 应用组件。7. 总结与扩展方向通过本次“反向极简”的拆解我们实现了一个仅用 300 token 系统提示词和 4 个核心工具的 Pi Agent。它清晰地展示了 AI Agent 的核心工作流理解意图 - 规划行动 - 调用工具 - 整合结果。与 Claude Code 等重型方案相比它的优势在于极致的透明、可控和低成本非常适合集成到特定工作流或作为学习 AI Agent 原理的起点。下一步你可以从以下几个方向深化增强工具能力将write_code/explain_code/refactor_code的存根替换为真正调用 LLM 的子函数实现“Agent 调用 LLM 工具该工具再调用 LLM”的链式思考。集成真实的search_web使用 Serper API 获取实时信息。添加run_python_code工具在安全沙箱中让 Agent 可以执行它生成的代码并看到结果。添加analyze_error工具输入错误信息输出解决方案。改进 Agent 逻辑实现ReAct (Reasoning Acting)模式让 LLM 在调用工具前先输出思考过程“Chain-of-Thought”这能提高复杂任务的成功率。引入记忆机制让 Agent 能记住跨会话的关键信息。实现多 Agent 协作例如一个 Agent 负责设计一个负责编码一个负责测试。优化工程架构使用LangChain或LlamaIndex等框架来管理工具链、记忆和提示词模板它们提供了更成熟的基础设施。为 Agent 构建一个Web API 接口使用 FastAPI 或 Flask方便其他系统调用。添加前端界面打造一个类似 ChatGPT 但具备专用工具的可交互 Web 应用。探索应用场景内部知识库问答将工具替换为检索公司内部文档。自动化运维工具集包括查询服务器状态、重启服务、查看日志。个性化学习助手工具集包括生成练习题、评估答案、推荐学习资料。AI Agent 的世界广阔无垠但核心往往在于对“工具”的精妙定义和对“意图”的准确理解。希望这个极简的 Pi Agent 能成为你探索这个世界的坚实起点。动手修改它添加你的第一个工具看看它能为你做什么。