本文探讨了人工智能(AI)在Solana开发中的应用,揭示了Solana独特架构对AI的挑战。文章提供了有效使用AI的策略,包括优化提示、提供上下文、逐步开发和迭代,提高代码的质量和效率。
15分钟阅读
2025年1月20日
人工智能 (AI) 正在改变我们构建软件的方式 . 它可以发现和修复bug,快速生成新功能,并处理一些我们不想做的琐事。像 Copilot 和 Cursor 这样的工具使得编程更快且不那么繁琐。但是,它们在与 Solana 一起工作时并不总是能很好地运行。
Solana 的架构 将其与大多数区块链区分开来 . 它并行处理交易,依赖账户而不是合约拥有状态,并使用程序派生地址 (Program-Derived Addresses, PDAs) 来增加安全性。如果你阅读了我们的博客文章,你会知道 在 Solana 上构建是与以太坊不同的。Solana 提供高吞吐量,并且在账户管理方面有独特的方法。这种区别常常会让在不同编码模式下训练的 AI 模型感到困惑。
本文将探讨 AI 为什么有时在 Solana 上遇到困难。它还将提供解决这些问题的方法。我们的主要关注点将是:
无论是新手还是有经验的人,这些建议都将帮助你在 Solana 上工作时获得更多 AI 的帮助。
Solana 的独特架构和要求可能会给 AI 带来问题。如果 AI 没有考虑到这些因素,你将浪费时间调试代码或重写程序,这违背了使用 AI 简化开发的目的。
Solana 主要在三个方面与众不同:
许多区块链,如以太坊,将数据直接绑定到智能合约的内部存储。相反,Solana 的编程模型 将一切组织围绕 “账户”。这些账户保存数据,而“程序”(Solana 的智能合约等价物)修改和与这些数据交互。
一个关键区别在于在 Solana 上,所有内容都是一个账户,甚至程序本身也是如此。程序仅仅是存储可执行数据的账户,这使得它们能够处理指令。相比之下,以太坊在外部拥有的账户 (EOA),由私钥控制,以及智能合约之间有自己的代码和内部存储。
这个根本差异可能会导致误解,尤其是在为 Solana 编写代码时。与以太坊智能合约一样,不熟悉账户模型的开发者可能会错误地假设一个程序可以拥有其数据。这种假设可能导致运行时错误或安全漏洞,当在 Solana 上部署时。
程序派生地址 (PDA) 是由程序 ID 和种子创建的。PDAs 使程序能够组织账户,而不需要私钥。然而,AI 模型可能看不出标准地址和 PDA 之间的区别。如果它们忘记处理种子或检查所有权,则 PDA 的 Rust 代码将是正确的,但却不是正确的 Solana 代码。
Solana 的运行时 Sealevel 使它能够并行处理交易,而不是传统区块链一笔一笔地处理交易。这是因为交易明确指定了它们将读取和写入的账户。通过这些信息,Solana 能够确定需要哪些账户锁定,从而安全地同时执行许多交易。
AI 常常误解这个模型。它可能假设交易将按顺序运行,就像在以太坊虚拟机(EVM)中一样。这可能导致代码未能优化并行性。由于 Solana 的基于账户的锁定系统与 EVM 的方法不同,AI 可能会编写表现不佳或无法扩展的代码。
开发者在使用 AI 构建 Solana 应用时会面临一些反复出现的问题:
AI 可能会提供在 Rust 中编译正常但在你将其部署到 Solana 时失败的功能。例如,AI 配对编程工具可能忽略签名者或错误解释账户引用。结果是?你只有在运行测试、实际交易或完成安全审核时才能发现的 bug 或 安全漏洞。
即使 AI 生成的代码有效,也可能没有被 优化为计算单位,导致交易处理缓慢,或者引入无法察觉的瓶颈。想象一下,AI 编写一个逐个更新账户的函数。它没有意识到,如果实现得当,Solana 可以同时处理它们。
AI 模型不知道你的文件夹结构或者你如何定义自定义数据类型,除非你告诉它。这意味着它们可能提出与现有设置发生冲突的解决方案,而将这些解决方案合并进你的项目可能会变得麻烦。
遵循以下建议,用 AI 构建 Solana 应用:
系统提示就像是 AI 的工作描述。它告诉 AI 其角色(例如," 你是一个在使用 Anchor 的 Solana 编程方面的专家...“)并概述你希望它遵循的规则。
以下是一个典型示例,稍作扩展:
代码
## 你是 Solana 程序开发的专家。
专注于使用 Rust 和 Anchor 构建和部署智能合约,
以及与 @solana/web3.js 集成链上数据。
---
### 一般指引
- 为 Solana 程序编写安全、高效且可维护的代码。
- 在部署之前彻底测试和审核所有程序。
### 使用 Rust 和 Anchor 的 Solana 程序开发
- 优先考虑 Rust 的安全性和性能。
- 使用 Anchor 宏简化账户管理、错误处理和数据序列化。
- 保持代码模块化,并清晰区分逻辑和数据。
### 安全和最佳实践
- 强制实施严格的访问控制–确保只有被允许的签名者能够修改数据。
- 负责任地使用 PDA:验证种子和所有权检查以防止冲突。
### 性能和优化
- 通过有效地打包操作来最小化交易成本。
- 利用并行处理–不要不必要地串行化步骤。
- 定期对代码进行基准测试,以发现并移除性能瓶颈。
一个写得好的系统提示就像一个蓝图–它使 AI 与 Solana 的架构保持一致。这确保每个响应都是准确、安全,并且符合你的需求。
当要求 AI 生成代码时,精确性至关重要。你越精确,结果就会越好。比较这些示例:
糟糕的提示:
"写一个初始化账户的函数。“
这个提示太模糊。AI 不知道编程语言、框架,或者这个提示是针对 Solana 代码的。它可能给出通用或无用的代码。
好的提示:
"写一个用 Rust 编写的函数,使用 Anchor 初始化一个 Solana 代币账户。使用从种子派生的 PDA。验证账户状态,并处理如果账户已经初始化时的错误。"
清晰的提示可以节省时间和精力。通过澄清 AI 的目标,你防止了程序后面所需的纠正工作以避免生成松散、不明确或错误目标的代码。它也最小化了错误解释,使与 AI 的工作更有效。
一个好的提示就像一张地图,引导 AI 走出误区,达到目标。
当你给 AI 提供适当的上下文时,它的工作效果会更好。许多工具允许你上传代码或链接文件。这有助于 AI 理解你的项目,创建符合你代码库的解决方案。
但不要给它过多信息。给 AI 提供太多信息–例如一个大型代码库–可能会导致它产生幻觉或产生不相关的响应。
相反,专注于提供你代码的相关部分。这将使 AI 保持专注,并提高其建议的质量。
例如,假设你的应用程序已经有一个文件定义了自定义数据结构:
代码
##[账户]
pub struct TokenAccount {
pub balance: u64,
pub is_initialized: bool,
// ... 更多字段
}
如果你上传这个文件或将其作为输入的一部分包含,AI 可以在生成的代码中使用你的精确字段名称 (balance
, is_initialized
)。
它还可以将其建议与项目的结构和约定对齐。
没有适当的上下文,像 “写一个函数来更新代币账户余额” 的提示可能会导致 AI 生成的代码如下:
代码
pub fn update_balance(ctx: Context<UpdateAccount>, amount: f64) -> Result<()> {
let account = &mut ctx.accounts.token_account;
account.amount += amount;
Ok(())
}
这段代码有两个问题:
首先,它使用 f64
作为 amount,而不是 u64
。
Solana 对本地浮点运算的支持非常有限。首选的做法是使用定点数,并根据使用的小数来缩放数字,这样可以避免性能和兼容性问题。
其次,它将余额字段称为 amount,这与结构不匹配。这可能导致将来的混淆与错误。
现在,如果你提供你的 TokenAccount 结构作为上下文,AI 可能会生成如下内容:
代码
pub fn update_balance(ctx: Context<UpdateAccount>, amount: u64) -> Result<()> {
let token_account = &mut ctx.accounts.token_account;
token_account.balance += amount;
Ok(())
}
这个输出与你的项目更好地对齐。
然而,即使有了这个改进,在 token_account.balance += amount
操作上添加一个 溢出检查 以确保你的代码的稳健性和安全性也是值得的。
记住以下步骤以向你的 AI 工具提供适当的上下文:
你不需要每次都从头开始。通常,你已经有一个可以工作的代码片段,但可以做得更好。AI 可以帮助你改进它。以下是一个示例:
这是原始代码:
代码
pub fn create_token_account(ctx: Context<CreateAccount>) -> Result<()> {
let token_account = &mut ctx.accounts.token_account;
token_account.balance = 0;
Ok(())
}
这段代码初始化一个代币账户并将余额设为零。它很简单,但没有处理诸如更新现有账户或检查所有权等场景。
你的提示:
"使这个函数更健壮。如果代币账户存在,将其加入余额。如果没有,请以特定金额初始化它。还要确保当前程序拥有该账户。"
这是你的提示后 AI 生成的代码:
代码
pub fn create_or_update_token_account(
ctx: Context<CreateAccount>,
amount: u64
) -> Result<()> {
let token_account = &mut ctx.accounts.token_account;
// 检查所有权
if token_account.owner != ctx.program_id {
return err!(ProgramError::IncorrectProgramId);
}
// 更新或初始化
if token_account.is_initialized {
token_account.balance += amount;
} else {
token_account.balance = amount;
token_account.is_initialized = true;
}
Ok(())
}
记住以下步骤,方便你将来采用这种方法:
分步骤构建代码可以帮助你避免错误并在进行的过程中精炼工作。不必试图一次性创建复杂的函数,而是将其分解为更小的任务。这使得测试和改进每一部分变得更容易,然后再继续。
让我们从简单的开始。请 AI 生成一个 Rust 函数,该函数使用种子计算程序派生地址 (PDA)。
这是一个例子提示:
"写一个 Rust 函数,从种子和程序 ID 计算 PDA,使用 Anchor。"
AI 的输出:
代码
pub fn find_pda(seed: &[u8], program_id: &Pubkey) -> Pubkey {
Pubkey::create_program_address(&[seed], program_id).unwrap()
}
这是一个不错的起点,但它不完整。让我们添加错误处理。
审查代码并识别问题。例如,使用 unwrap() 可能导致程序在种子无效时崩溃。
请 AI 修复这个问题:
"更新这个函数,添加对无效种子的错误处理。返回一个 Result 而不是直接 unwrap。"
更新后的代码:
代码
pub fn find_pda(seed: &[u8], program_id: &Pubkey) -> Result<Pubkey, ProgramError> {
Pubkey::create_program_address(&[seed], program_id)
.map_err(|_| ProgramError::InvalidSeeds)
}
现在,这个函数能优雅地处理无效的种子,通过返回错误而不是崩溃。
接下来,让我们集成这个函数。
请 AI 使用 PDA 函数在一个更大的例程中。例如:
"写一个使用这个 PDA 创建新代币账户的函数。验证 PDA 不能已经在使用中。"
这是 AI 的输出:
代码
pub fn create_token_account(ctx: Context<CreateAccount>, seed: &[u8], amount: u64) -> Result<()> {
let program_id = ctx.program_id;
let pda = find_pda(seed, &program_id)?;
if ctx.accounts.token_account.key != pda {
return Err(ProgramError::InvalidSeeds);
}
if !ctx.accounts.token_account.is_initialized {
let token_account = &mut ctx.accounts.token_account;
token_account.balance = amount;
token_account.is_initialized = true;
}
Ok(())
}
这一步将 PDA 逻辑集成到账户创建中,同时确保 PDA 不被不当地重复使用。
现在,让我们优化和完善代码。请 AI 进一步优化函数,使用提示如:
"优化此函数以最小化交易成本,并包含所有权验证。"
这是 AI 的输出:
代码
pub fn create_token_account(ctx: Context<CreateAccount>, seed: &[u8], amount: u64) -> Result<()> {
let program_id = ctx.program_id;
let pda = find_pda(seed, &program_id)?;
if ctx.accounts.token_account.key != pda || ctx.accounts.token_account.owner != program_id {
return Err(ProgramError::IncorrectProgramId);
}
if !ctx.accounts.token_account.is_initialized {
let token_account = &mut ctx.accounts.token_account;
token_account.balance = amount;
token_account.is_initialized = true;
}
Ok(())
}
最终函数现在安全、高效,并符合 Solana 的最佳实践。
记住这些提示,下次你与 AI 进行配对编程时:
在 Solana 上使用 AI 构建应用可以非常强大——前提是你采取细致周到的方法。Solana 的架构与其他区块链不同——它使用 PDA、并行交易处理和基于账户的设计。这意味着你需要正确引导 AI。
从清晰的提示开始。明确告诉 AI 你想要的具体内容,并具体说明使用的工具如 Rust、Anchor 和 PDA。一个好的提示可能会说:
“ 编写一个 Rust 函数,以一个 PDA 初始化 Solana 代币账户,验证账户状态,并处理错误。”
这种清晰度使 AI 更有可能生成适合 Solana 的有效代码。
始终提供上下文。分享项目的代码或说明你使用的结构,比如账户模式或数据类型。例如,如果你有一个 TokenAccount
结构,请包含其详细信息,以便 AI 能对其输出进行对齐。但不要给它过多信息——专注于相关部分以保持建议的准确性和价值。
将任务分为小步骤。通过迭代过程逐步改进代码。从简单的任务开始,如计算 PDA,然后在此基础上增加错误处理或账户验证。每一步后测试。通过这样做,你可以尽早捕捉问题,稳步前进。
最后,仔细检查 AI 的工作。大多数时候,AI 生成的代码有些小问题,如缺少溢出检查或遗漏验证。确保输出遵循 Solana 的规范,保持无 bug,并利用并行处理能力。
AI 只是一个起点;你必须完成工作。通过清晰的指令、上下文、逐步开发和细致的审查,你可以利用 AI 创建安全且优化的 Solana 程序。保持参与这个过程,AI 将成为一个节省时间并编写更好代码的有价值工具。
要了解更多关于使用 AI 在 Solana 上构建的知识,请探索以下资源:
- 原文链接: helius.dev/blog/how-to-u...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!