Código pronto para usar

Conecte seu agente de voz por telefone ao seu código

Copie e cole o código que conecta seu agente de voz por telefone ao seu repositório no GitHub ou a uma pasta no seu computador. Antes de qualquer dado chegar ao modelo de IA, um filtro de privacidade substitui senhas, chaves, e-mails, números de telefone e outros dados privados por marcadores e depois restaura os valores reais na resposta, no seu ambiente.

Lê: flows/, prompts/, intents/, README.md
Executa a cada pull request (ou manualmente) e publica a resposta sem dados sensíveis em um comentário
Qualquer modelo compatível com OpenAI

1 Adicione os arquivos

Crie uma pasta agent/ no seu repositório e salve privacy-filter.ts e agent-github.ts nela.

2 Adicione sua chave de IA

No GitHub: Settings → Secrets and variables → Actions → adicione AI_API_KEY. Configure AI_BASE_URL e AI_MODEL para usar qualquer provedor compatível com OpenAI.

3 Ative a GitHub Action

Salve o fluxo de trabalho como .github/workflows/voice-phone-agent.yml. Executa a cada pull request (ou manualmente) e publica a resposta sem dados sensíveis em um comentário.

agent/agent-github.ts

Lê seu repositório do GitHub, oculta dados privados, consulta o modelo e salva ou publica a resposta.

/**
 * Voice phone agent: reads your GitHub repository, scrubs secrets and personal data, then asks the AI model.
 *
 *   npm install @octokit/rest tsx
 *   GITHUB_TOKEN=… GITHUB_REPOSITORY=owner/repo AI_API_KEY=… npx tsx agent/agent-github.ts
 *
 * Optional: AI_BASE_URL and AI_MODEL (any OpenAI-compatible API), PR_NUMBER or ISSUE_NUMBER
 * (the GitHub Action sets these for you).
 */
import { writeFileSync } from "node:fs";
import { Octokit } from "@octokit/rest";
import { isSensitivePath, restore, scrub, type Vault } from "./privacy-filter";

const AGENT = {
  "label": "Voice phone agent",
  "system": "You are a voice phone agent. Your job: check call scripts and conversation flows for dead ends and missing intents. Text like [EMAIL_1] or [API_KEY_2] is a placeholder for private data that was removed before you saw it. Keep placeholders exactly as written and never guess the real values.",
  "task": "Find dead ends, missing intents and confusing prompts in the call flows.",
  "include": [
    "flows/",
    "prompts/",
    "intents/",
    "README.md"
  ],
  "baseUrl": "https://api.openai.com/v1",
  "model": "gpt-5-mini",
  "keyFallback": "",
  "autoPost": true,
  "audit": false
};

const MAX_CHARS = 60_000;
const BINARY = /\.(png|jpe?g|gif|webp|ico|pdf|zip|gz|mp4|mp3|woff2?|lock)$/i;

function env(key: string, fallback?: string): string {
  const value = process.env[key] || fallback;
  if (!value) throw new Error(`Missing environment variable ${key}`);
  return value;
}

const [owner, repo] = env("GITHUB_REPOSITORY").split("/") as [string, string];
const gh = new Octokit({ auth: env("GITHUB_TOKEN") });
const vault: Vault = new Map(); // placeholder → real value; never leaves this machine

const wanted = (path: string) =>
  !isSensitivePath(path) && !BINARY.test(path) && AGENT.include.some((p) => path === p || path.startsWith(p));

async function readFile(path: string): Promise<string> {
  const { data } = await gh.repos.getContent({ owner, repo, path });
  if (Array.isArray(data) || data.type !== "file" || !("content" in data)) return "";
  return Buffer.from(data.content, "base64").toString("utf8");
}

async function collectContext(): Promise<string> {
  const parts: string[] = [];
  const size = () => parts.reduce((n, p) => n + p.length, 0);

  if (process.env.PR_NUMBER) {
    const files = await gh.paginate(gh.pulls.listFiles, { owner, repo, pull_number: Number(process.env.PR_NUMBER), per_page: 100 });
    for (const f of files) if (!isSensitivePath(f.filename) && f.patch) parts.push(`### Changed: ${f.filename}\n${f.patch}`);
  }
  if (process.env.ISSUE_NUMBER) {
    const { data } = await gh.issues.get({ owner, repo, issue_number: Number(process.env.ISSUE_NUMBER) });
    parts.push(`### Issue #${data.number}: ${data.title}\n${data.body ?? ""}`);
  }

  const { data: info } = await gh.repos.get({ owner, repo });
  const { data: tree } = await gh.git.getTree({ owner, repo, tree_sha: info.default_branch, recursive: "true" });
  for (const item of tree.tree) {
    if (size() > MAX_CHARS) break;
    if (item.type !== "blob" || !item.path || (item.size ?? 0) > 200_000 || !wanted(item.path)) continue;
    parts.push(`### ${item.path}\n${await readFile(item.path)}`);
  }
  return parts.join("\n\n").slice(0, MAX_CHARS);
}

async function askModel(context: string) {
  const safe = scrub(context, { vault }); // the model only ever sees safe.text
  const res = await fetch(`${env("AI_BASE_URL", AGENT.baseUrl)}/chat/completions`, {
    method: "POST",
    headers: { "Content-Type": "application/json", Authorization: `Bearer ${env("AI_API_KEY", AGENT.keyFallback)}` },
    body: JSON.stringify({
      model: env("AI_MODEL", AGENT.model),
      messages: [
        { role: "system", content: AGENT.system },
        { role: "user", content: `${AGENT.task}\n\n${safe.text}` },
      ],
    }),
  });
  if (!res.ok) throw new Error(`AI model error ${res.status}: ${await res.text()}`);
  const json = (await res.json()) as { choices: { message: { content: string } }[] };
  return { reply: json.choices[0]?.message.content ?? "", findings: safe.findings };
}

async function main() {
  const { reply, findings } = await askModel(await collectContext());
  console.log("Privacy filter hid:", findings.map((f) => `${f.count} × ${f.label}`).join(", ") || "nothing");

  writeFileSync("agent-report.md", restore(reply, vault)); // real values, for you only
  writeFileSync("agent-report.safe.md", reply); // placeholders, safe to share
  if (AGENT.audit) writeFileSync("privacy-audit.json", JSON.stringify({ at: new Date().toISOString(), findings }, null, 2));

  const target = Number(process.env.PR_NUMBER || process.env.ISSUE_NUMBER || 0);
  if (target && AGENT.autoPost) {
    // Post the scrubbed version so private values never end up in a public comment.
    await gh.issues.createComment({ owner, repo, issue_number: target, body: reply });
    console.log(`Commented on #${target}`);
  } else {
    console.log("Saved agent-report.md. A person should review it before anything is sent.");
  }
}

main().catch((err) => {
  console.error(err);
  process.exit(1);
});

Veja o que seu agente de voz por telefone veria

Cole um código, um arquivo de configuração ou uma mensagem de cliente. À esquerda está o que você tem; à direita, tudo o que o modelo de IA veria.

Executa no seu navegador, sem enviar nada
O que a IA vê
// config.ts
export const db = "[CONNECTION_STRING_1]";
const apiKey = "[API_KEY_1]";
STRIPE_SECRET_KEY=[API_KEY_2]

// Ticket from [EMAIL_1], phone [PHONE_1]
5 itens privados ocultos:
1 × Link com senha
2 × Chave de API
1 × Endereço de e-mail
1 × Número de telefone

Código para outros agentes de IA