文章摘要
文章介绍了如何利用Pydantic-AI框架和Model Context Protocol(MCP)构建自定义的CLI编码代理,强调了自建工具相较于商业产品的优势,如更贴合特定需求,并展示了如何通过AWS Bedrock或其他主流提供商实现这一目标。
文章总结
构建你自己的CLI编码助手:使用Pydantic-AI
如果你尝试过Claude Code、Gemini Code、Open Code或Simon Willison的LLM CLI,你会发现它们与ChatGPT或Github Copilot有本质的不同。这些工具不仅仅是聊天机器人或自动补全工具,它们是能够读取代码、运行测试、搜索文档并异步修改代码库的智能助手。
那么,它们是如何工作的呢?对我来说,理解任何工具的最佳方式就是尝试自己构建一个。因此,我们决定动手实践,本文将带你了解如何使用Pydantic-AI框架和模型上下文协议(MCP)构建我们自己的CLI编码助手。你将不仅看到如何组装这些组件,还会了解每个功能的重要性以及它如何改变你与代码的交互方式。
为什么选择自己构建?
在深入技术实现之前,我们先探讨一下为什么选择自己构建解决方案。使用我们的自定义助手后,答案很快变得清晰:虽然商业工具令人印象深刻,但它们是为通用场景设计的。我们的助手则完全根据我们内部环境和特定项目的独特需求进行了定制。更重要的是,构建它让我们深入了解了这些系统的工作原理,并提升了我们自己的生成式AI平台和开发工具的质量。
架构概述
我们的编码助手由以下几个关键组件构成:
- 核心AI模型:通过AWS Bedrock访问的Anthropic的Claude模型。
- Pydantic-AI框架:提供代理框架和许多实用工具,使我们的助手立即变得有用。
- MCP服务器:独立的进程,为助手提供专用工具,MCP是定义这些服务器的通用标准。
- CLI接口:用户与助手的交互方式。
通过模型上下文协议(MCP),AI模型可以通过标准化接口使用各种工具,这使得我们的助手具有高度可扩展性——我们可以通过实现额外的MCP服务器轻松添加新功能。
从简单开始:基础搭建
我们首先创建了一个基本的项目结构,并安装了必要的依赖项:
bash
uv init
uv add pydantic_ai
uv add boto3
主要依赖包括:
pydantic-ai:用于构建AI代理的框架。boto3:用于与AWS API交互。
我们选择了Anthropic的Claude Sonnet 4(通过AWS Bedrock访问)作为基础模型,因为它具有强大的代码理解和生成能力。
第一个功能:测试
与其在每次编码迭代后自己运行测试,不如让助手来完成。我们通过以下代码实现了这一功能:
python
import subprocess
@agent.tool_plain()
def run_unit_tests() -> str:
"""Run unit tests using uv."""
result = subprocess.run(
["uv", "run", "pytest", "-xvs", "tests/"], capture_output=True, text=True
)
return result.stdout
现在,当我说“X不工作”时,助手会:
- 运行测试套件。
- 识别哪些测试失败。
- 分析错误信息。
- 提出针对性的修复建议。
添加智能:指令和意图
我们意识到需要教助手更多关于我们开发哲学的内容,并引导它远离不良行为。通过添加指令,助手现在理解了我们在测试驱动开发和最小化更改方面的价值观。
MCP革命:可插拔功能
通过MCP,我们的助手从有用的助手转变为接近商业CLI助手的工具。MCP允许我们通过运行专用服务器来添加复杂功能。
沙盒化Python执行
为了避免执行任意代码的风险,我们添加了Pydantic AI的默认服务器,用于沙盒化Python代码执行,这使得计算更加可靠。
最新库文档
我们添加了Context7,以便助手可以访问最新的Python库文档,而不是依赖可能过时的训练数据。
AWS MCP
由于这个助手是为AWS平台构建的,我们添加了AWS Labs的MCP服务器,以便助手可以直接访问AWS文档来帮助解决配置问题。
互联网搜索
我们添加了通用互联网搜索功能,使助手能够搜索最新的Stack Overflow讨论、GitHub问题或最佳实践。
结构化问题解决
我们添加了代码推理MCP,帮助助手系统地思考复杂问题,而不是直接跳到解决方案。
优化推理
随着我们添加了更多复杂功能,我们注意到推理和分析任务通常比常规文本生成耗时更长,因此我们调整了Bedrock配置,使其更加耐心。
桌面指挥官
通过添加桌面指挥官MCP服务器,我们的助手可以从一个有用的助手转变为能够在开发环境中实际执行任务的工具。
完整系统
最终的助手配置如下:
```python import asyncio import subprocess import boto3 from pydanticai import Agent from pydanticai.mcp import MCPServerStdio from pydanticai.models.bedrock import BedrockConverseModel from pydanticai.providers.bedrock import BedrockProvider from botocore.config import Config as BotocoreConfig
bedrockconfig = BotocoreConfig( readtimeout=300, connecttimeout=60, retries={"maxattempts": 3}, ) bedrockclient = boto3.client( "bedrock-runtime", regionname="eu-central-1", config=bedrockconfig ) model = BedrockConverseModel( "eu.anthropic.claude-sonnet-4-20250514-v1:0", provider=BedrockProvider(bedrockclient=bedrock_client), ) agent = Agent( model=model, )
instructions = """ You are a specialised agent for maintaining and developing the XXXXXX codebase.
Development Guidelines:
Test Failures:
- When tests fail, fix the implementation first, not the tests
- Tests represent expected behavior; implementation should conform to tests
- Only modify tests if they clearly don't match specifications
Code Changes:
- Make the smallest possible changes to fix issues
- Focus on fixing the specific problem rather than rewriting large portions
- Add unit tests for all new functionality before implementing it
Best Practices:
- Keep functions small with a single responsibility
- Implement proper error handling with appropriate exceptions
- Be mindful of configuration dependencies in tests
Remember to examine test failure messages carefully to understand the root cause before making any changes. """
runpython = MCPServerStdio( "deno", args=[ "run", "-N", "-R=nodemodules", "-W=node_modules", "--node-modules-dir=auto", "jsr:@pydantic/mcp-run-python", "stdio", ], )
internetsearch = MCPServerStdio(command="uvx", args=["duckduckgo-mcp-server"]) codereasoning = MCPServerStdio( command="npx", args=["-y", "@mettamatt/code-reasoning"], toolprefix="codereasoning", ) desktopcommander = MCPServerStdio( command="npx", args=["-y", "@wonderwhy-er/desktop-commander"], toolprefix="desktopcommander", ) awslabs = MCPServerStdio( command="uvx", args=["awslabs.core-mcp-server@latest"], env={"FASTMCPLOGLEVEL": "ERROR"}, toolprefix="awslabs", ) awsdocs = MCPServerStdio( command="uvx", args=["awslabs.aws-documentation-mcp-server@latest"], env={"FASTMCPLOGLEVEL": "ERROR", "AWSDOCUMENTATIONPARTITION": "aws"}, toolprefix="awsdocs", ) context7 = MCPServerStdio( command="npx", args=["-y", "@upstash/context7-mcp"], toolprefix="context" )
agent = Agent( instructions=instructions, model=model, mcpservers=[ runpython, internetsearch, codereasoning, context7, awslabs, awsdocs, desktopcommander, ], )
@agent.toolplain() def rununittests() -> str: """Run unit tests using uv.""" result = subprocess.run( ["uv", "run", "pytest", "-xvs", "tests/"], captureoutput=True, text=True ) return result.stdout
async def main(): async with agent.runmcpservers(): await agent.to_cli()
if name == "main": asyncio.run(main()) ```
工作流的变化
- 调试变得协作化:你有一个智能伙伴,可以分析错误信息、提出假设并帮助测试解决方案。
- 学习加速:当处理不熟悉的库或模式时,助手可以解释现有代码、提出改进建议,并教你为什么某些方法更好。
- 上下文切换减少:你有一个单一的界面,可以访问所有资源,同时保持对你特定问题的上下文。
- 问题解决变得结构化:助手可以将复杂问题分解为逻辑步骤,探索替代方案,并解释其推理过程。
- 代码审查改进:助手可以在你提交之前审查你的更改,发现潜在问题并提出改进建议。
我们学到的关于CLI助手的见解
构建我们自己的助手揭示了这一新兴范式的几个关键点:
- MCP几乎是你所需要的:魔力不在于任何单一功能,而在于它们如何协同工作。
- 当前信息至关重要:访问实时搜索和最新文档使助手在现实开发工作中更加可靠。
- 结构化思维很重要:代码推理能力将助手从聪明的自动补全工具转变为能够分解复杂问题并探索替代解决方案的思考伙伴。
- 上下文为王:商业助手之所以令人印象深刻,部分原因在于它们能够在所有不同工具之间保持上下文。
- 专业化很重要:我们的助手在我们的特定代码库上比通用工具表现得更好,因为它理解我们的模式、惯例和工具偏好。
未来展望
CLI助手范式仍在快速发展。我们正在探索的一些领域包括:
- AWS特定工具:AWS Labs的MCP服务器为云原生开发提供了惊人的深度。
- 工作流增强:教助手我们的常见开发工作流,使其能够端到端处理常规任务。
- 基准测试:使用Terminal Bench来测试我们的助手与大型商业工具的对比。
为什么这很重要
CLI编码助手代表了从AI作为写作助手到AI作为开发伙伴的根本转变。与Copilot的自动补全或ChatGPT的问答不同,这些助手可以:
- 理解你的整个项目上下文。
- 跨多个工具执行任务。
- 在复杂工作流中保持状态。
- 从你的特定代码库和模式中学习。
构建你自己的助手——即使是一个简单版本——可以让你深入了解这项技术的未来方向,并在商业工具到来时充分利用它们。
软件开发的未来不仅仅是更快地编写代码,而是拥有一个智能伙伴,它理解你的目标、约束和代码库,能够帮助你思考问题并协作实施解决方案。而理解这一未来的最佳方式?就是自己动手构建它。
评论总结
评论内容总结:
正面评价:
- 易用性与功能:多位用户认为Pydantic-AI在构建代理时非常方便,尤其是其高级API。例如,binalpatel提到“Pydantic-AI is lovely... it does make constructing any given agent very easy”,bluecoconut也表示“overall it's been working great for me”。
- 团队响应与支持:mritchie712提到团队对bug和功能请求的响应非常迅速,“The team is very responsive to bugs / feature requests”。
负面评价:
- 底层API问题:一些用户认为底层API使用起来较为繁琐。binalpatel指出“the lower level APIs are a little painful to use”,iLoveOncall也表示“anything beyond basic usage is an absolute chore”。
- 兼容性与bug:siva7提到在使用某些特定功能时会出现bug,“once you use some more niche feature the bugs do shine through”,photonthug也提到“vanilla pydantic-ai rarely does, regardless of the number of retries”。
中立与建议:
- 文档与体验:binalpatel提到最初选择Pydantic-AI是因为对LiteLLM的文档和体验不满意,“just being disgusted at how poor the documentation/experience of using LiteLLM was”。
- 未来改进:photonthug虽然遇到问题,但仍保留了对Pydantic-AI的依赖,希望未来有所改进,“didn’t remove the pydantic-ai dependency completely because I’m hoping something changes”。
其他观点:
- LLM与Pydantic的兼容性:gck1提到LLM在处理Pydantic模型时容易出错,“LLMs seem to trip a lot over Pydantic’s magic”。
- AI与编程趋势:bgwalter将AI编程与敏捷开发、设计模式等趋势相提并论,“'AI' and vibe coding fits very well into that list”。
总结:Pydantic-AI在易用性和功能上受到好评,但在底层API、兼容性和文档方面存在不足。用户对其未来改进持开放态度,同时也指出了LLM与Pydantic模型兼容性的问题。