Sempre que  visito fóruns, salas de bate papo e outras relacionadas a programação em  Delphi, vejo que uma das dúvidas mais comuns é como projetar um bom  sistema, e automático, de chave auto-incremento no Firebird/Interbase.  Já vi muita coisa e acho que uma das soluções que eu adotei é bem  produtiva e muito eficiente. O conceito não é novo mas eu nunca vi assim  como vou propor.
Bom um dos primeiros passos para um bom  sistema é uma boa padronização do BD. Toda a nomenclatura dos meus BD eu  padronizo. Uma delas, é que mais nos interessa: o generator. O meu é  formado da seguinte maneira (sempre) "GEN_[tabela]_ID". Seguindo esse  mesmo conceito eu também padronizo a nomenclatura dos meus objetos no  Delphi. SQLQuery = qryTABELA, DataSetProvider = dspTABELA, ClientDataSet  = cdsTABELA. Eu também acho muito mais produtivo separar os DMs dessa  forma: um RDM onde vai ficar as Queries e os DataSetProviders, assim  facilmente poderemos "transformar" nossa aplicação CS em Multi-Tier, e  um DM onde ficam os CDSs. Como a propriedade ProviderName do CDS é uma  string ele não vai "ver" os DSP do RDM, assim colocamos o componente  TLocalConnection no RDM em um TConnectionBroker no DM e apontamos sua  propriedade Connection para o RDM->TLocalConnection. De posse dessas  informações podemos prosseguir.
No RDM onde estão todos os DSP, aponte  BeforeUpdateRecord de todos os DSP para o mesmo evento.
Essa é a assinatura do evento:
BeforeUpdateRecord(Sender: TObject; SourceDS:  TDataSet; 
  DeltaDS:  TCustomClientDataSet; UpdateKind: TUpdateKind; 
  var Applied:  Boolean);
E no evento codifique assim:
Crie uma variável Campo do tipo TField e  uma variável SQLStmt do tipo String e tbm uma variável Tabela do tipo  String e por fim uma variável CustomSQLDataSet do tipo  TCustomSQLDataSet. Não se esqueça de declarar e unit DB.
case  UpdateKind of
ukInsert: // Inserindo um registro
  begin
    Campo := DeltaDS.Fields[0];
    if  (Campo.DataType = ftInteger) and Campo.IsNull then
    begin
      Tabela  :=  UpperCase(IProviderSupport(SourceDS).PSGetTableName);
      SQLStmt := Format(SELECT  GEN_ID(GEN_%s_ID, 1) FROM RDB$DATABASE, [Tabela]);
      SQLCon.Execute(SQLStmt, nil, @CustomSQLDataSet);
      if  Assigned(CustomSQLDataSet) then
      begin
        DeltaDS.Edit;
        Campo.NewValue  := CustomSQLDataSet.Fields[0].AsInteger;
        DeltaDS.Post;
        SysUtils.FreeAndNil(CustomSQLDataSet);
      end;
     end;
   end;
Veja que em SQLStmt existe GEN_%s_ID, por  isso eu uso um padrão de nomenclatura para os meus Generators no banco.  Primeiro ele verifica se o campo[0], que eu sempre deixo a primary key,  é do tipo Inteiro e se está nulo, porque eu posso ter atribuído o valor  da chave antes, num caso em que vc tem Pedido e Ítens de Pedido, o  valor da chave tem que ser atribuido antes, depois executo o SQLStmt  apontando o ResultSet, que é um ponteiro, para o CustomSQLDataSet.
Se a pesquisa retornar dados o  CustomSQLDataSet vai ter o valor do generator incrementado, aí é só  atribuir o valor para o Delta.
Pronto, sua aplicação ja tem um campo  auto-incremento totalmente automatizado !
Um abraço e até a próxima