MoreRSS

site iconHackerNoonModify

We are an open and international community of 45,000+ contributing writers publishing stories and expertise for 4+ million curious and insightful monthly readers.
Please copy the RSS to your reader, or quickly subscribe to:

Inoreader Feedly Follow Feedbin Local Reader

Rss preview of Blog of HackerNoon

如何使用 shadcn/ui 构建分析管理仪表盘框架

2026-04-25 23:03:23

In this guide, we will walk you through building a production-ready analytics admin dashboard shell using shadcn/ui, Next.js, and Base UI. You'll scaffold the full layout sidebar navigation, statistics widgets, sales charts, earnings reports, and a product data table using a single CLI command and a clean, component-driven architecture.

Why Admin Dashboards Are Hard to Get Right

Admin dashboards are among the most requested UI blocks in web development and among the most underestimated.

On the surface, you need a sidebar, some charts, and a data table. But the moment you start building, the complexity compounds: responsive breakpoints, accessibility in dropdowns, collapsible nav states, consistent spacing, dark mode, and making sure the whole thing doesn't fall apart when real data flows in.

Most teams either spend weeks building from scratch or bolt together mismatched component libraries that fight each other in production. Neither is ideal.

This is exactly the problem that shadcn/ui was designed to address, and the Shadcn Dashboard ecosystem has extended that philosophy further with a set of composable, production-grade dashboard blocks built on both Radix UI and Base UI primitives.

In this guide, you'll install a complete analytics dashboard shell in minutes, then understand exactly how each piece fits together so you can customize it confidently.

What You'll Build

By the end of this tutorial, you'll have a fully functional analytics admin dashboard shell that includes:

  • A collapsible app sidebar with a navigation menu
  • A site header with user and notification dropdowns
  • Statistics widgets for key metrics at a glance
  • A Sales Overview Chart for monthly trend visualization
  • An Earning Report Chart for revenue breakdown
  • A Top Products Table with sortable product data
  • A Sales by Country Widget for geographic distribution

The entire layout is built with a 12-column CSS Grid system, scales across screen sizes, and follows shadcn/ui's composable pattern, meaning you own the code and can extend it as needed.

Prerequisites

Before getting started, make sure you have:

  • Node.js 18+ installed
  • A Next.js project was initialized (npx create-next-app@latest)
  • shadcn/ui configured in your project (run npx shadcn@latest init if you haven't already)
  • A package manager of your choice: npm, pnpm, yarn, or bun

If you need a refresher on setting up shadcn/ui from scratch, the official shadcn/ui docs are the best reference.

Step 1 - Install the Dashboard Shell via CLI

The dashboard-shell-01 block is available directly through the shadcn CLI. Run the appropriate command for your package manager:

pnpm:

pnpm dlx shadcn@latest add @shadcn-space/dashboard-shell-01

npm:

npx shadcn@latest add @shadcn-space/dashboard-shell-01

yarn:

yarn dlx shadcn@latest add @shadcn-space/dashboard-shell-01

bun:

bunx --bun shadcn@latest add @shadcn-space/dashboard-shell-01

This single command scaffolds the complete block into your project components, types, assets, and all. No manual copy-pasting of boilerplate.

Note: Getting started guides and official Shadcn CLI documentation page. Check there for version-specific instructions.

Step 2 - Understand the Folder Structure

After installation, you'll see the following structure added to your project:

app/
  dashboard-shell-01/
    page.tsx
components/
  shadcn-space/
    blocks/
      dashboard-shell-01/
        app-sidebar.tsx
        site-header.tsx
        nav-main.tsx
        statistics.tsx
        sales-overview-chart.tsx
        earning-report-chart.tsx
        top-product-table.tsx
        salesbycountrywidget.tsx
        user-dropdown.tsx
        notification-dropdown.tsx
assets/
  logo/
    logo.tsx

This is a clean, flat component structure. Each file has a single responsibility, making it easy to swap out, restyle, or replace individual pieces without affecting the rest of the dashboard.

Step 3 - Review the Page Entry Point

The page entry point at app/dashboard-shell-01/page.tsx controls how everything works together. It imports all the dashboard blocks and composes them into a responsive 12-column grid:

import AppSidebar from "@/components/shadcn-space/blocks/dashboard-shell-01/app-sidebar";
import StatisticsBlock from "@/components/shadcn-space/blocks/dashboard-shell-01/statistics";
import SalesOverviewChart from "@/components/shadcn-space/blocks/dashboard-shell-01/sales-overview-chart";
import EarningReportChart from "@/components/shadcn-space/blocks/dashboard-shell-01/earning-report-chart";
import TopProductTable from "@/components/shadcn-space/blocks/dashboard-shell-01/top-product-table";
import SalesByCountryWidget from "@/components/shadcn-space/blocks/dashboard-shell-01/salesbycountrywidget";
export default function Page() {
  return (
&nbsp;&nbsp;&nbsp;&nbsp;<AppSidebar>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<div className="grid grid-cols-12 gap-6 p-6 max-w-7xl mx-auto">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<div className="col-span-12">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<StatisticsBlock />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</div>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<div className="xl:col-span-8 col-span-12">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<SalesOverviewChart />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</div>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<div className="xl:col-span-4 col-span-12">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<EarningReportChart />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</div>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<div className="xl:col-span-8 col-span-12">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<TopProductTable />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</div>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<div className="xl:col-span-4 col-span-12">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<SalesByCountryWidget />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</div>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</div>
&nbsp;&nbsp;&nbsp;&nbsp;</AppSidebar>
&nbsp;&nbsp;);
}

Let's break down the layout decisions here.

AppSidebar as the layout wrapper. The sidebar is the outermost shell. It provides the persistent navigation structure and wraps all dashboard content. This is a common pattern - your sidebar owns the layout, and your content slots in as children.

Widgets stack on smaller screens and shift into a main + side layout on larger screens for a clean, structured dashboard.

max-w-7xl mx-auto. The content area is capped at 7xl (1280px) and centered. This prevents the layout from becoming uncomfortably wide on very large monitors.

Step 4 - Walk Through Each Component

app-sidebar.tsx - The Layout Shell

This is the most complex component in the shell. It renders the collapsible sidebar navigation and wraps the main content area. Under the hood, it uses Base UI primitives (not just Radix) for the sidebar panel, which gives you finer control over accessibility and animation behavior.

Key features to look for in this component:

  • Sidebar open/close state management
  • Navigation link active state
  • Mobile-responsive collapse behavior
  • Slot for the SiteHeader at the top of the content area

nav-main.tsx - Navigation Menu

This component renders the sidebar's navigation items. It's separated from app-sidebar.tsx intentionally keeping nav data separate from layout logic makes it easy to swap out your routes or add role-based navigation visibility without touching the sidebar shell.

In a real application, you'd feed this component your route definitions from a config file or API response.

site-header.tsx - Top Bar

The site header sits at the top of the main content area (not the sidebar). It typically includes:

  • Page title or breadcrumb
  • NotificationDropdown - for user alerts
  • UserDropdown - for profile actions like settings and logout

Both dropdowns are separate components, keeping the header itself clean and easy to maintain.

statistics.tsx - KPI Widgets

The statistics block renders a row of key performance indicator (KPI) cards. These typically show metrics like total revenue, active users, orders, and conversion rate, the "at a glance" numbers that executives and product teams check first thing.

Each stats card is self-contained and accepts a value, label, trend indicator, and icon. You'll want to connect these to your real data layer (an API route, SWR hook, or React Query call) once you're out of the prototype phase.

sales-overview-chart.tsx - Time-Series Sales Chart

This component renders a line or bar chart showing sales trends over time, typically weekly or monthly. It takes up 8 columns on desktop, making it the dominant visual on the page.

The chart is built to accept time-series data arrays. Swap in your actual sales data from your backend, and the chart will reflect it automatically.

earning-report-chart.tsx - Revenue Breakdown

The earnings report chart takes up the remaining 4 columns next to the sales overview. It typically shows a donut or bar chart breaking down revenue by category, channel, or product line.

This component pairs naturally with the sales overview, one shows the trend, the other shows the composition.

top-product-table.tsx - Data Table

The top products table is a full-featured data table with sortable columns, product names, sales figures, revenue values, and status indicators. It uses shadcn/ui's Table primitives, which are built on accessible HTML <table> elements with clean styling applied on top.

For production use, wire this up to a paginated API endpoint and add TanStack Table for server-side sorting and filtering. The TanStack Table integration guide on HackerNoon is a great next read once you have this shell running.

salesbycountrywidget.tsx - Geographic Distribution

The final widget in the 4-column slot shows a breakdown of sales by country. This can be a ranked list with progress bars, a choropleth map, or a simple table, depending on your data volume and visual needs.

For smaller datasets, the ranked list with inline progress bars (which this component uses by default) is the most readable format.

Step 5 - Customizing the Dashboard

Now that the shell is running, here are the most common customizations you'll want to make.

Updating Navigation Items

Open nav-main.tsx and update the navigation array with your actual routes:

const navItems = [
&nbsp;&nbsp;{ title: "Overview", href: "/dashboard", icon: LayoutDashboard },
&nbsp;&nbsp;{ title: "Analytics", href: "/dashboard/analytics", icon: BarChart3 },
&nbsp;&nbsp;{ title: "Orders", href: "/dashboard/orders", icon: ShoppingCart },
&nbsp;&nbsp;{ title: "Customers", href: "/dashboard/customers", icon: Users },
&nbsp;&nbsp;{ title: "Settings", href: "/dashboard/settings", icon: Settings },
];

Use Lucide React for icons, it's already included with shadcn/ui and has excellent tree-shaking support.

Connecting Real Data

Each widget component accepts props or uses internal state with mock data. To connect real data, create a server component wrapper or use a data-fetching hook:

// Server component approach (Next.js)
import { getSalesOverview } from "@/lib/api/sales";
export default async function SalesOverviewWrapper() {
&nbsp;&nbsp;const data = await getSalesOverview();
&nbsp;&nbsp;return <SalesOverviewChart data={data} />;
}

For client-side data fetching, SWR or TanStack Query works cleanly with these components.

Theming

The dashboard shell respects shadcn/ui's CSS variable theming system. Update your globals.css to change the color palette:

:root {
&nbsp;&nbsp;--primary: 221.2 83.2% 53.3%;
&nbsp;&nbsp;--primary-foreground: 210 40% 98%;
&nbsp;&nbsp;/* ... other tokens */
}

Dark mode is supported out of the box via the dark class on the <html> element.

The Base UI Difference

One thing worth highlighting: this dashboard shell is built on both Radix UI and Base UI primitives and specifically uses the Base UI version for key interactive components.

Base UI (from the MUI team) offers headless, unstyled components with a focus on accessibility and animation control. Compared to Radix alone, it gives you:

  • More granular control over transitions and animation states
  • A smaller bundle footprint for complex interactive patterns
  • Strong ARIA compliance out of the box

This matters for dashboards because dropdowns, popovers, tooltips, and modals are core to the UX, and accessibility in these patterns is frequently underinvested. Using Base UI primitives means you're starting from a solid foundation rather than fighting with aria-* attributes manually.

Viewing the Full Source Code

If you want to preview the complete, unmodified source code for the dashboard shell before or after installation, visit this dashboard shell page.

The page shows the full component code with syntax highlighting, making it easy to reference individual files without cloning your project.

Live Preview

Shadcn Analytics Admin Dashboard Shell

What to Build Next

Once your analytics dashboard shell is running with real data, here are the logical next extensions:

Add authentication. Wrap your dashboard routes with a session check using NextAuth.js or Clerk. The sidebar and header already have slots for user state - connect them to your auth provider.

Add a date range picker. Most analytics dashboards need temporal filtering. shadcn/ui ships a DateRangePicker component that integrates cleanly with the chart components here.

Add TanStack Table for the product table. The default table component is great for static or small datasets. For server-side pagination, sorting, and filtering, TanStack Table is the standard choice in the shadcn/ui ecosystem.

Add real-time updates. If your data changes frequently, consider Supabase Realtime, Pusher, or Server-Sent Events to push live updates to the statistics widgets without a full page refresh.

Dark mode toggle. Add a theme toggle button to the site header's action area. The next-themes library handles this in about 10 lines of code.

Summary

Building a production-grade analytics admin dashboard used to mean weeks of work assembling a sidebar, finding compatible chart libraries, wrestling with responsive grids, and handling accessibility edge cases in dropdowns and modals.

The dashboard-shell-01 block improves things significantly. One CLI command gives you a complete, composable shell built on shadcn/ui and Base UI primitives with all the structural decisions already made correctly.

The result is a dashboard you actually own (not a black-box library dependency) that you can read, understand, and extend confidently.

The full block library, including this dashboard shell and others, lives at Shadcn Space, worth bookmarking if you're regularly building UI-heavy React and Next.js applications.

AI警犬安防模拟系统通过构建工业区自主安防模拟,获得23.64的实用性评分

2026-04-25 22:53:41

This interview explores an AI-powered security dog simulation built using Python and Finite State Machine logic to model autonomous patrol, pursuit, and fail-safe behaviors in industrial environments. While currently a functional prototype, the system demonstrates key concepts like energy-aware decision-making and autonomous recovery protocols. The project aims to evolve toward real-world deployment by integrating obstacle navigation, hardware sensors, and modular open-source frameworks for broader adoption.

适用于 Google 生态系统的 PersonaOps

2026-04-25 22:42:27

Voice-to-Data Intelligence System Integrated with Google Cloud, Workspace, and Gemini APIs


Table of Contents

  1. Abstract
  2. Introduction: From Notion-Centric to Google-Ecosystem Architecture
  3. System Architecture: The Google-Integrated Pipeline
  4. Core Integration Layers
    • 4.1 Voice Input & Speech-to-Text: Google Cloud Speech-to-Text API
    • 4.2 Intent & Entity Extraction: Gemini API with Function Calling
    • 4.3 Schema Management: Google Sheets as Dynamic Schema Registry
    • 4.4 Data Persistence: Google Workspace as Operational Store
    • 4.5 Workflow Automation: Google Apps Script & Cloud Functions
    • 4.6 Personal Intelligence: Cross-Application Context Reasoning
  5. Implementation Tutorials: Building PersonaOps on Google
    • 5.1 Tutorial 1: Real-Time Voice Agent with Gemini and Google ADK
    • 5.2 Tutorial 2: Email Triage Pipeline with Gemini Function Calling
    • 5.3 Tutorial 3: Sheets-Based Schema Evolution with Apps Script
    • 5.4 Tutorial 4: Cross-Ecosystem Intelligence with Personal Intelligence Beta
    • 5.5 Tutorial 5: Document Generation from Voice-Derived Structured Data
  6. Application Ecosystem: Product Classification by Use Case
    • 6.1 Field Operations & Mobile Capture
    • 6.2 Enterprise Knowledge Management
    • 6.3 AI Agent Memory Systems
    • 6.4 Business Intelligence & Analytics
    • 6.5 Developer Workflow Automation
  7. Technical Challenges & Google-Specific Mitigations
  8. Development Pathways: From MVP to Enterprise Scale
  9. Living Ecosystem: Compound Intelligence Across Google Services
  10. References

Abstract

PersonaOps for Google Ecosystem extends the core PersonaOps voice-to-data intelligence architecture by replacing Notion MCP with Google Cloud and Workspace services as the primary orchestration layer. This integration leverages Gemini's function calling capabilities, Google Cloud Speech-to-Text, Workspace APIs (Sheets, Docs, Gmail, Drive), and emerging capabilities like Personal Intelligence to create a voice-native data intelligence system that operates seamlessly across the Google ecosystem.

The system treats voice input as a structured data ingestion channel that dynamically generates, populates, and evolves schemas stored in Google Sheets, persists records across Workspace applications, and enables cross-application reasoning through Gemini's Personal Intelligence beta. Unlike the Notion-centric implementation, this Google-integrated architecture benefits from enterprise-grade scalability, built-in AI model access, and native integration with the productivity tools used by over 3 billion users worldwide.

This whitepaper provides practical, tutorial-based implementation guidance referencing official Google documentation, codelabs, and verified integration patterns. Each component is grounded in Google's published API specifications and developer resources, ensuring reproducibility and production-readiness.


1. Introduction: From Notion-Centric to Google-Ecosystem Architecture

1.1 The Google Ecosystem Advantage

While the original PersonaOps architecture positioned Notion as the central control plane, Google's ecosystem offers distinct advantages for voice-to-data intelligence systems at scale:

  • Native AI Integration: Gemini models are directly accessible via the same APIs used for Workspace automation, eliminating the need for separate LLM provider integrations .
  • Unified Identity & Security: Google Cloud IAM and Workspace authentication provide consistent security boundaries across all services.
  • Enterprise Scalability: Google Cloud infrastructure scales horizontally to support thousands of concurrent voice streams.
  • Cross-Application Intelligence: The Personal Intelligence beta enables Gemini to reason across Gmail, Photos, Search history, and YouTube without explicit user direction .

1.2 Conceptual Architecture Shift

The Google-integrated PersonaOps architecture replaces Notion's MCP layer with a distributed orchestration layer spanning:

| Original PersonaOps Component | Google Ecosystem Replacement | |----|----| | Notion Schema Registry | Google Sheets with Apps Script versioning | | Notion Data Store | Google Sheets (structured) / Docs (unstructured) / Drive (files) | | Notion Workflow Engine | Apps Script triggers + Cloud Functions + Cloud Workflows | | Notion Human-in-the-Loop UI | Google Sheets UI + Google Docs comments | | Notion MCP Server | Gemini Function Calling + Workspace APIs |

This shift maintains all core PersonaOps capabilities—voice-to-schema generation, adaptive evolution, human-in-the-loop correction—while adding enterprise deployment capabilities and cross-application intelligence.


2. System Architecture: The Google-Integrated Pipeline

The following diagram represents the Google-integrated PersonaOps pipeline, with official Google APIs and services mapped to each processing stage: \n

┌─────────────────────────────────────────────────────────────────────────────┐
│                    PERSONAOPS FOR GOOGLE ECOSYSTEM                          │
│                         Voice-to-Data Pipeline                              │
└─────────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────┐
│      [USER VOICE INPUT]          │ ◄── Microphone / WebRTC / Meet
└────────────────┬─────────────────┘
                 │ Raw Audio Stream (16kHz PCM)
                 ▼
┌──────────────────────────────────┐
│  [GOOGLE CLOUD SPEECH-TO-TEXT]   │ ◄── StreamingRecognize API
│  - Chirp model (latest)          │     Speaker diarization
│  - Real-time transcription       │     Partial + final transcripts
└────────────────┬─────────────────┘
                 │ Raw Transcript
                 ▼
┌──────────────────────────────────┐
│ [GEMINI API - FUNCTION CALLING]  │ ◄── gemini-3.1-pro-preview
│  - Intent classification         │     Structured JSON output
│  - Entity extraction             │     Custom function declarations
└────────────────┬─────────────────┘
                 │ Typed Intent + Entities
                 ▼
┌──────────────────────────────────┐
│ [SCHEMA MANAGEMENT - SHEETS API] │ ◄── spreadsheets.values
│  - Schema registry lookup        │     Apps Script versioning
│  - Dynamic column addition       │     Non-breaking migrations
└────────────────┬─────────────────┘
                 │ Target Sheet + Column Mapping
                 ▼
┌──────────────────────────────────┐
│  [DATA PERSISTENCE - WORKSPACE]  │ ◄── Sheets API (batchUpdate)
│  - Structured: Google Sheets     │     Docs API (batchUpdate)
│  - Unstructured: Google Docs     │     Drive API (file creation)
│  - Attachments: Google Drive     │
└────────────────┬─────────────────┘
                 │
                 ▼
┌──────────────────────────────────┐
│ [WORKFLOW AUTOMATION LAYER]      │ ◄── Apps Script triggers
│  - Time-based processing         │     Cloud Functions (eventarc)
│  - Webhook receivers             │     Cloud Workflows (orchestration)
└────────────────┬─────────────────┘
                 │
    ┌────────────┼────────────────────────┐
    ▼            ▼                        ▼
┌─────────┐ ┌──────────┐    ┌─────────────────────────┐
│ BigQuery│ │ Looker   │    │ [PERSONAL INTELLIGENCE] │
│(Analyt- │ │ Studio   │    │  Cross-app reasoning     │
│ ics)    │ │(Dash-    │    │  Gmail + Photos + Search │
└─────────┘ │ boards)  │    └─────────────────────────┘
            └──────────┘

2.1 Key Architectural Differences from Notion Implementation

  1. Speech-to-Text: Uses Google's Chirp model, which is integrated with Vertex AI and supports speaker diarization natively .
  2. Intent Processing: Gemini's function calling replaces custom NLP classification, providing native JSON schema enforcement .
  3. Schema Registry: Google Sheets with Apps Script versioning replaces Notion databases—offering unlimited rows, programmatic schema evolution, and spreadsheet-native human review.
  4. Workflow Engine: Apps Script triggers + Cloud Functions provide more flexible automation than Notion's built-in automation.
  5. Cross-App Intelligence: Personal Intelligence beta enables voice queries that reason across Gmail, Photos, and Search history without explicit context switching .

3. Core Integration Layers

3.1 Voice Input & Speech-to-Text: Google Cloud Speech-to-Text API

Google Cloud Speech-to-Text provides the audio transcription layer for PersonaOps. The Chirp model (Google's most advanced speech model) supports:

  • Streaming recognition with partial transcript delivery for latency reduction
  • Speaker diarization to identify individual speakers in multi-user environments
  • Domain-specific model adaptation for industry terminology

Implementation Pattern (Python)

from google.cloud import speech_v1p1beta1 as speech
from google.cloud.speech_v1p1beta1 import types

# Configure streaming recognition with diarization
client = speech.SpeechClient()
config = types.RecognitionConfig(
    encoding=types.RecognitionConfig.AudioEncoding.LINEAR16,
    sample_rate_hertz=16000,
    language_code="en-US",
    model="latest_long",  # Chirp model
    enable_speaker_diarization=True,
    diarization_speaker_count=2,
    enable_automatic_punctuation=True,
)

streaming_config = types.StreamingRecognitionConfig(
    config=config,
    interim_results=True,  # Enable partial transcripts
)

# Process streaming audio
def process_audio_stream(audio_generator):
    requests = (types.StreamingRecognizeRequest(audio_content=chunk) 
                for chunk in audio_generator)
    responses = client.streaming_recognize(streaming_config, requests)

    for response in responses:
        for result in response.results:
            if result.is_final:
                # Final transcript with speaker tags
                yield extract_final_transcript(result)
            else:
                # Partial transcript for speculative processing
                yield extract_partial_transcript(result)

Reference: Google Cloud Speech-to-Text documentation on Vertex AI .


3.2 Intent & Entity Extraction: Gemini API with Function Calling

Gemini's function calling capability serves as the semantic core of PersonaOps, converting transcripts into structured intents and typed entities. The model determines when to call specific functions and provides JSON-structured parameters for execution .

Function Declaration Pattern

Define function declarations that map to PersonaOps operations: \n

from google import genai
from google.genai import types

# Define the function declaration for CREATE intent
create_record_function = {
    "name": "create_personaops_record",
    "description": "Creates a new structured record from voice input in the appropriate Google Sheet",
    "parameters": {
        "type": "object",
        "properties": {
            "table_name": {
                "type": "string",
                "description": "Target sheet/table name (e.g., 'Sales_Log', 'Field_Report')"
            },
            "fields": {
                "type": "object",
                "description": "Key-value pairs of field names and typed values",
                "additionalProperties": True
            },
            "confidence": {
                "type": "number",
                "description": "Confidence score 0-1 for this extraction"
            }
        },
        "required": ["table_name", "fields"]
    }
}

# Define schema modification function
modify_schema_function = {
    "name": "modify_personaops_schema",
    "description": "Adds, renames, or removes columns from a PersonaOps-managed sheet",
    "parameters": {
        "type": "object",
        "properties": {
            "table_name": {"type": "string"},
            "action": {"type": "string", "enum": ["ADD_COLUMN", "RENAME_COLUMN", "REMOVE_COLUMN"]},
            "column_name": {"type": "string"},
            "column_type": {"type": "string", "enum": ["TEXT", "NUMBER", "DATE", "CURRENCY", "SELECT"]},
            "new_name": {"type": "string"}  # For rename operations
        },
        "required": ["table_name", "action", "column_name"]
    }
}

# Define query function
query_function = {
    "name": "query_personaops_data",
    "description": "Queries PersonaOps-managed sheets with filters and returns results",
    "parameters": {
        "type": "object",
        "properties": {
            "table_name": {"type": "string"},
            "filters": {
                "type": "array",
                "items": {
                    "type": "object",
                    "properties": {
                        "field": {"type": "string"},
                        "operator": {"type": "string", "enum": ["equals", "contains", "greater_than", "less_than", "on_or_after"]},
                        "value": {"type": "string"}
                    }
                }
            },
            "limit": {"type": "integer", "default": 10}
        },
        "required": ["table_name"]
    }
}

# Configure client with tools
client = genai.Client()
tools = types.Tool(function_declarations=[
    create_record_function,
    modify_schema_function,
    query_function
])
config = types.GenerateContentConfig(
    tools=[tools],
    thinking_level="high"  # Gemini 3.1 Pro feature for complex reasoning
)

# Process transcript
response = client.models.generate_content(
    model="gemini-3.1-pro-preview",
    contents=f"Process this voice transcript: '{transcript}'",
    config=config
)

Reference: Gemini Function Calling Documentation and Gemini 3.1 Pro API Guide .

Entity Type Mapping

Gemini's function calling enforces type validation through JSON schema:

| Extracted Entity Type | Gemini Parameter Type | Google Sheets Format | |----|----|----| | INTEGER | {"type": "integer"} | Number | | CURRENCY | {"type": "number"} | Number with currency format | | STRING | {"type": "string"} | Text | | DATETIME | {"type": "string", "format": "date-time"} | Date/DateTime | | ENUM | {"type": "string", "enum": [...]} | Dropdown (Data Validation) | | PERSON | {"type": "string"} + context | Text with @mention | | BOOLEAN | {"type": "boolean"} | Checkbox |


3.3 Schema Management: Google Sheets as Dynamic Schema Registry

Google Sheets serves as PersonaOps' schema registry, storing table definitions, column metadata, and version history.

Schema Registry Structure

Create a master "PersonaOpsSchemaRegistry" sheet with the following columns:

| TableName | SheetID | Version | Created | LastModified | ColumnsJSON | RowCount | |----|----|----|----|----|----|----| | SalesLog | 1aBcDeF… | 3 | 2026-01-15 | 2026-03-21 | {"Quantity":"NUMBER","UnitPrice":"CURRENCY"…} | 247 | | FieldReport | 2xYzAbC… | 1 | 2026-02-01 | 2026-02-01 | {"Location":"TEXT","Observation":"TEXT"…} | 18 |

Apps Script Schema Evolution

/**
 * Adds a new column to a PersonaOps-managed sheet (non-breaking migration)
 * @param {string} tableName - Name of the table/sheet
 * @param {string} columnName - New column name
 * @param {string} columnType - Type: TEXT, NUMBER, DATE, CURRENCY, SELECT
 * @param {Array} options - For SELECT type, array of allowed values
 */
function addColumnToSchema(tableName, columnName, columnType, options = []) {
  const registrySheet = SpreadsheetApp.getActive()
    .getSheetByName('PersonaOps_Schema_Registry');

  // Find table in registry
  const data = registrySheet.getDataRange().getValues();
  let tableRow, sheetId, currentVersion, columnsJson;

  for (let i = 1; i < data.length; i++) {
    if (data[i][0] === tableName) {
      tableRow = i + 1;
      sheetId = data[i][1];
      currentVersion = data[i][2];
      columnsJson = JSON.parse(data[i][5]);
      break;
    }
  }

  if (!sheetId) throw new Error(`Table ${tableName} not found in registry`);

  // Check if column already exists (idempotent)
  if (columnsJson[columnName]) {
    console.log(`Column ${columnName} already exists`);
    return;
  }

  // Update columns JSON
  columnsJson[columnName] = columnType;

  // Open target sheet and add column
  const targetSheet = SpreadsheetApp.openById(sheetId).getSheets()[0];
  const lastCol = targetSheet.getLastColumn();
  targetSheet.getRange(1, lastCol + 1).setValue(columnName);

  // Apply data validation for SELECT type
  if (columnType === 'SELECT' && options.length > 0) {
    const rule = SpreadsheetApp.newDataValidation()
      .requireValueInList(options, true)
      .build();
    targetSheet.getRange(2, lastCol + 1, targetSheet.getMaxRows() - 1, 1)
      .setDataValidation(rule);
  }

  // Update registry
  registrySheet.getRange(tableRow, 3).setValue(currentVersion + 1);
  registrySheet.getRange(tableRow, 5).setValue(new Date());
  registrySheet.getRange(tableRow, 6).setValue(JSON.stringify(columnsJson));

  // Log schema evolution event
  logSchemaEvolution(tableName, 'ADD_COLUMN', columnName, columnType, currentVersion + 1);
}

Reference: Google Sheets API batchUpdate documentation and Apps Script Spreadsheet Service.


3.4 Data Persistence: Google Workspace as Operational Store

PersonaOps routes different data types to appropriate Workspace applications:

Structured Data → Google Sheets

from googleapiclient.discovery import build
from google.oauth2.credentials import Credentials

def append_structured_record(sheet_id: str, fields: dict, idempotency_key: str = None):
    """Append a structured record to a PersonaOps sheet with idempotency check"""
    sheets = build('sheets', 'v4', credentials=creds)

    # Idempotency check (prevent duplicate voice entries)
    if idempotency_key:
        existing = sheets.spreadsheets().values().get(
            spreadsheetId=sheet_id,
            range="A:A"  # Assuming first column is ID
        ).execute().get('values', [])
        existing_ids = {row[0] for row in existing if row}
        if idempotency_key in existing_ids:
            return {"status": "duplicate", "id": idempotency_key}

    # Map fields to column order based on schema registry
    ordered_values = map_fields_to_columns(sheet_id, fields)

    # Append row
    result = sheets.spreadsheets().values().append(
        spreadsheetId=sheet_id,
        range="A1",
        valueInputOption="USER_ENTERED",
        insertDataOption="INSERT_ROWS",
        body={"values": [ordered_values]}
    ).execute()

    return {"status": "success", "updated_range": result['updates']['updatedRange']}

Unstructured Content → Google Docs

For voice inputs containing narrative content (meeting notes, observations), PersonaOps creates or updates Google Docs: \n

// Apps Script - Append AI-summarized voice content to a Google Doc
function appendVoiceNoteToDoc(docId, transcript, aiSummary, speaker, timestamp) {
  const doc = DocumentApp.openById(docId);
  const body = doc.getBody();

  // Format entry with speaker attribution
  const entry = body.appendParagraph(`${speaker} - ${timestamp.toLocaleString()}`);
  entry.setHeading(DocumentApp.ParagraphHeading.HEADING3);

  body.appendParagraph(`"${transcript}"`).setItalic(true);
  body.appendParagraph(`AI Summary: ${aiSummary}`);
  body.appendParagraph('---');

  // Add comment for human review if low confidence
  // (using Docs API for comments)
}

Reference: Skywork.ai Google Workspace automation tutorials .


3.5 Workflow Automation: Google Apps Script & Cloud Functions

PersonaOps uses a hybrid automation approach:

| Trigger Type | Implementation | Use Case | |----|----|----| | Time-based | Apps Script triggers | Batch processing of pending records | | Data-change | Sheets onEdit() | Real-time human override detection | | Webhook | Cloud Functions (Eventarc) | External system integration | | Schedule | Cloud Scheduler + Cloud Functions | Periodic sync to BigQuery |

Human Override Detection Pattern

// Apps Script - Detect manual edits to AI-populated fields
function onEdit(e) {
  const sheet = e.source.getActiveSheet();
  const range = e.range;
  const newValue = e.value;
  const row = range.getRow();
  const col = range.getColumn();

  // Check if this sheet is PersonaOps-managed
  const registryData = getRegistryEntry(sheet.getName());
  if (!registryData) return;

  // Get the original AI-populated value from audit log
  const originalValue = getOriginalValue(sheet.getName(), row, col);

  if (originalValue && originalValue !== newValue) {
    // Log human override
    logHumanOverride({
      table: sheet.getName(),
      row: row,
      column: registryData.columns[col - 1],
      original_value: originalValue,
      human_value: newValue,
      timestamp: new Date()
    });

    // Propagate to external systems via webhook
    propagateCorrection(sheet.getName(), row, col, newValue);
  }
}

Reference: Apps Script Triggers documentation and Cloud Functions Eventarc integration.


3.6 Personal Intelligence: Cross-Application Context Reasoning

Google's Personal Intelligence beta (January 2026) enables Gemini to reason across Gmail, Photos, Search history, and YouTube to provide contextually enriched responses .

PersonaOps Integration Pattern

def enrich_voice_context_with_personal_intelligence(transcript: str, user_id: str):
    """
    Leverage Personal Intelligence to enrich voice-derived data with
    cross-application context before schema mapping.
    """
    # Personal Intelligence is a model capability, not a separate API
    # It activates when the user has enabled app connections in Gemini

    client = genai.Client()

    # The model automatically accesses connected apps when relevant
    response = client.models.generate_content(
        model="gemini-3.1-pro-preview",
        contents=f"""
        User {user_id} said: "{transcript}"

        Use available connected apps (Gmail, Photos, Search history) to:
        1. Verify or enrich any mentioned entities
        2. Provide missing context (e.g., full names from email contacts)
        3. Identify relevant past interactions or documents

        Return enriched context as JSON.
        """,
        config=types.GenerateContentConfig(
            thinking_level="high",
            response_mime_type="application/json"
        )
    )

    return json.loads(response.text)

Example Use Case (from Google's announcement ):

User says: "Log a tire purchase for my car"

Personal Intelligence:

  • Retrieves tire size from Photos (picture of tire specification)
  • Identifies vehicle from Gmail (service appointment confirmation)
  • Suggests all-weather tire category based on family road trip photos

PersonaOps records enriched data: {vehicle: "2022 Honda Odyssey", tire_size: "235/60R18", tire_type: "All-Weather", category: "Maintenance"}

Reference: TechCrunch coverage of Personal Intelligence beta .


4. Implementation Tutorials: Building PersonaOps on Google

4.1 Tutorial 1: Real-Time Voice Agent with Gemini and Google ADK

Source: Google Cloud Blog - "Build a real-time voice agent with Gemini & ADK"

Objective: Create a voice-enabled PersonaOps capture agent using Gemini and the Agent Development Kit (ADK).

Architecture Components:

  • Gemini model with ADK for agent orchestration
  • WebSocket for bidirectional audio streaming
  • Google Search tool for real-time context enrichment
  • MCP Toolset for Google Maps (location-aware data capture)

Implementation Steps: \n

# From Google's official tutorial 
from google.adk.agents import Agent
from google.adk.tools import GoogleSearch, MCPToolset
from google.adk.tools.mcp_tool.mcp_toolset import StdioServerParameters
from google.adk.agents.run_config import RunConfig, StreamingMode
from google.genai import types

# 1. Define PersonaOps-specific system instruction
SYSTEM_INSTRUCTION = """
You are PersonaOps Voice Agent, converting spoken observations into structured data.

When users speak:
1. Identify the intent: CREATE record, UPDATE record, QUERY data, or MODIFY schema
2. Extract all entities with appropriate types
3. Call the appropriate PersonaOps function with structured parameters
4. Confirm the action with the user

Available tables: Sales_Log, Field_Report, Client_Notes, Inventory
"""

# 2. Configure agent with tools
agent = Agent(
    name="personaops_voice_agent",
    model="gemini-3.1-pro-preview",
    instruction=SYSTEM_INSTRUCTION,
    tools=[
        GoogleSearch,  # For real-time entity validation
        MCPToolset(
            connection_params=StdioServerParameters(
                command='npx',
                args=["-y", "@modelcontextprotocol/server-google-maps"],
                env={"Maps_API_KEY": MAPS_API_KEY}
            ),
        )
    ],
)

# 3. Configure bidirectional streaming for natural conversation
run_config = RunConfig(
    streaming_mode=StreamingMode.BIDI,  # Allows user interruption
    speech_config=types.SpeechConfig(
        voice_config=types.VoiceConfig(
            prebuilt_voice_config=types.PrebuiltVoiceConfig(
                voice_name="en-US-Neural2-F"  # Natural voice
            )
        )
    ),
    response_modalities=["AUDIO"],
    output_audio_transcription=types.AudioTranscriptionConfig(),
    input_audio_transcription=types.AudioTranscriptionConfig(),
)

# 4. Asynchronous task management for real-time performance
async with asyncio.TaskGroup() as tg:
    tg.create_task(receive_client_messages(), name="ClientMessageReceiver")
    tg.create_task(send_audio_to_service(), name="AudioSender")
    tg.create_task(receive_service_responses(), name="ServiceResponseReceiver")

PersonaOps-Specific Extensions:

Add custom function declarations for PersonaOps operations: \n

# Add to agent tools
personaops_functions = [
    create_record_function,  # Defined in Section 3.2
    modify_schema_function,
    query_function
]

agent = Agent(
    # ... existing config ...
    tools=[
        GoogleSearch,
        MCPToolset(...),
        *personaops_functions  # Custom PersonaOps functions
    ]
)

Reference: Official Google Cloud Blog tutorial .


4.2 Tutorial 2: Email Triage Pipeline with Gemini Function Calling

Source: Skywork.ai - "Automate Email Triage, Sheets Updates & Report Assembly"

Objective: Create an automated pipeline that classifies incoming emails and extracts structured data into PersonaOps sheets.

Integration with PersonaOps: \n

from googleapiclient.discovery import build
from google.oauth2.credentials import Credentials
import google.generativeai as genai

# 1. Configure Gemini for email classification with function calling
def classify_email_and_extract(subject: str, body: str, sender: str):
    """Classify email intent and extract structured data using Gemini"""

    client = genai.Client()

    response = client.models.generate_content(
        model="gemini-3.1-pro-preview",
        contents=f"""
        Analyze this email:
        From: {sender}
        Subject: {subject}
        Body: {body[:1000]}

        Determine:
        1. Intent: CREATE_RECORD, UPDATE_RECORD, QUERY, or IGNORE
        2. Target table from: Sales_Log, Client_Notes, Field_Report, Support_Ticket
        3. Extract structured fields based on the target table's schema
        """,
        config={
            "tools": [{
                "functionDeclarations": [create_record_function]
            }],
            "response_mime_type": "application/json"
        }
    )

    return json.loads(response.text)

# 2. Gmail processing loop
def process_personaops_emails():
    gmail = build('gmail', 'v1', credentials=creds)

    # Query unprocessed PersonaOps emails
    query = 'label:personaops-pending -label:personaops-processed'
    messages = gmail.users().messages().list(userId='me', q=query).execute()

    for msg in messages.get('messages', []):
        # Get email content
        email_data = gmail.users().messages().get(
            userId='me', id=msg['id'], format='full'
        ).execute()

        # Extract headers and body
        headers = email_data['payload']['headers']
        subject = next(h['value'] for h in headers if h['name'] == 'Subject')
        sender = next(h['value'] for h in headers if h['name'] == 'From')

        # Classify and extract with Gemini
        extracted = classify_email_and_extract(subject, get_body(email_data), sender)

        if extracted.get('intent') == 'CREATE_RECORD':
            # Append to appropriate PersonaOps sheet
            append_structured_record(
                sheet_id=get_sheet_id(extracted['table_name']),
                fields=extracted['fields'],
                idempotency_key=msg['id']  # Use email ID for deduplication
            )

        # Mark as processed
        gmail.users().messages().modify(
            userId='me',
            id=msg['id'],
            body={
                'addLabelIds': ['Label_123456'],  # personaops-processed label
                'removeLabelIds': ['Label_789012']  # personaops-pending label
            }
        ).execute()

Reference: Skywork.ai tutorial with Claude Haiku patterns adapted for Gemini .


4.3 Tutorial 3: Sheets-Based Schema Evolution with Apps Script

Source: Adapted from Skywork.ai "Automate Google Workspace Pipelines"

Objective: Implement non-breaking schema evolution in Google Sheets triggered by voice commands.

Complete Apps Script Implementation: \n

/**
 * PersonaOps Schema Evolution Engine for Google Sheets
 * Triggered by Gemini function calls from voice input
 */

// Schema Registry Structure (stored in Properties Service for persistence)
const SCHEMA_REGISTRY_KEY = 'PERSONAOPS_SCHEMA_REGISTRY';

/**
 * Initialize or load schema registry
 */
function getSchemaRegistry() {
  const props = PropertiesService.getScriptProperties();
  const stored = props.getProperty(SCHEMA_REGISTRY_KEY);

  if (stored) {
    return JSON.parse(stored);
  }

  // Initialize empty registry
  return {
    tables: {},
    version: 1,
    migrations: []
  };
}

/**
 * Add column to existing sheet (non-breaking migration)
 * Called when user says "Add [column] field to [table]"
 */
function addColumnToTable(tableName, columnName, columnType, options = []) {
  const registry = getSchemaRegistry();

  // Validate table exists
  if (!registry.tables[tableName]) {
    throw new Error(`Table '${tableName}' not found in schema registry`);
  }

  const tableInfo = registry.tables[tableName];
  const sheet = SpreadsheetApp.openById(tableInfo.sheetId).getSheets()[0];

  // Check if column already exists (idempotent)
  const headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];
  if (headers.includes(columnName)) {
    console.log(`Column '${columnName}' already exists in '${tableName}'`);
    return { status: 'exists', table: tableName, column: columnName };
  }

  // Add column header
  const newColIndex = headers.length + 1;
  sheet.getRange(1, newColIndex).setValue(columnName);

  // Apply formatting based on type
  const dataRange = sheet.getRange(2, newColIndex, sheet.getMaxRows() - 1, 1);

  switch(columnType) {
    case 'DATE':
      dataRange.setNumberFormat('yyyy-mm-dd');
      break;
    case 'CURRENCY':
      dataRange.setNumberFormat('$#,##0.00');
      break;
    case 'SELECT':
      if (options.length > 0) {
        const rule = SpreadsheetApp.newDataValidation()
          .requireValueInList(options, true)
          .build();
        dataRange.setDataValidation(rule);
      }
      break;
    case 'CHECKBOX':
      dataRange.insertCheckboxes();
      break;
  }

  // Update registry
  tableInfo.columns[columnName] = {
    type: columnType,
    options: options,
    added_at: new Date().toISOString(),
    added_in_version: registry.version
  };
  tableInfo.version += 1;
  registry.version += 1;

  // Record migration
  registry.migrations.push({
    table: tableName,
    action: 'ADD_COLUMN',
    column: columnName,
    type: columnType,
    timestamp: new Date().toISOString(),
    version: tableInfo.version
  });

  // Persist registry
  saveSchemaRegistry(registry);

  return {
    status: 'success',
    table: tableName,
    column: columnName,
    version: tableInfo.version
  };
}

/**
 * Create new table from voice-described schema
 */
function createTableFromVoice(tableName, fields) {
  const registry = getSchemaRegistry();

  // Check if table already exists
  if (registry.tables[tableName]) {
    throw new Error(`Table '${tableName}' already exists`);
  }

  // Create new spreadsheet
  const ss = SpreadsheetApp.create(`PersonaOps - ${tableName}`);
  const sheet = ss.getSheets()[0];

  // Set up headers and formatting
  const headers = Object.keys(fields);
  sheet.getRange(1, 1, 1, headers.length).setValues([headers]);

  // Apply column formatting
  headers.forEach((colName, index) => {
    const colType = fields[colName];
    const range = sheet.getRange(2, index + 1, sheet.getMaxRows() - 1);

    // Apply type-specific formatting (similar to addColumnToTable)
    applyColumnFormatting(range, colType);
  });

  // Freeze header row
  sheet.setFrozenRows(1);

  // Add alternating row colors for readability
  sheet.getRange('A:Z').applyRowBanding();

  // Register in schema registry
  registry.tables[tableName] = {
    sheetId: ss.getId(),
    sheetUrl: ss.getUrl(),
    columns: fields,
    version: 1,
    created_at: new Date().toISOString(),
    row_count: 0
  };
  registry.version += 1;

  saveSchemaRegistry(registry);

  return {
    status: 'created',
    table: tableName,
    sheetUrl: ss.getUrl(),
    sheetId: ss.getId()
  };
}

/**
 * Persist schema registry to Script Properties
 */
function saveSchemaRegistry(registry) {
  const props = PropertiesService.getScriptProperties();
  props.setProperty(SCHEMA_REGISTRY_KEY, JSON.stringify(registry));
}

Reference: Apps Script patterns from Skywork.ai tutorial .


4.4 Tutorial 4: Cross-Ecosystem Intelligence with Personal Intelligence Beta

Source: TechCrunch - "Gemini's new beta feature provides proactive responses"

Objective: Leverage Personal Intelligence to enrich voice-derived data with cross-application context.

Implementation Pattern: \n

from google import genai
from google.genai import types

class PersonaOpsPersonalIntelligence:
    """
    Enriches voice data using Gemini's Personal Intelligence capability,
    which reasons across Gmail, Photos, Search, and YouTube history.
    """

    def __init__(self):
        self.client = genai.Client()

    def enrich_voice_transcript(self, transcript: str, user_context: dict) -> dict:
        """
        Process voice input with Personal Intelligence context.

        Personal Intelligence automatically accesses:
        - Gmail: for contact info, past communications, appointments
        - Photos: for visual context, object recognition, location
        - Search history: for recent topics of interest
        - YouTube: for watched content related to query
        """

        # Construct prompt that activates Personal Intelligence
        prompt = f"""
        [PERSONAL INTELLIGENCE CONTEXT]
        User: {user_context.get('name', 'Unknown')}
        Voice input: "{transcript}"
        Current location: {user_context.get('location', 'Unknown')}
        Current time: {user_context.get('timestamp')}

        Using available connected apps (Gmail, Photos, Search, YouTube):
        1. Identify any missing context needed for data extraction
        2. Retrieve relevant information (contacts, past interactions, visual data)
        3. Enrich the voice-derived entities with this context

        Return enriched data as JSON with fields:
        - intent: CREATE_RECORD / UPDATE_RECORD / QUERY / SCHEMA_MODIFY
        - table: Target table name
        - entities: Key-value pairs with typed, enriched values
        - context_source: Which app provided enrichment (gmail/photos/search/youtube)
        - confidence: 0-1 score
        """

        response = self.client.models.generate_content(
            model="gemini-3.1-pro-preview",
            contents=prompt,
            config=types.GenerateContentConfig(
                thinking_level="high",
                response_mime_type="application/json"
            )
        )

        return json.loads(response.text)

    def proactive_schema_suggestion(self, recent_activity: list) -> list:
        """
        Analyze recent cross-app activity to suggest new schema fields.

        Example: If user has been emailing about "delivery dates" and
        searching for "shipping status", suggest adding tracking_number
        and estimated_delivery columns.
        """

        prompt = f"""
        [PROACTIVE SCHEMA ANALYSIS]
        Recent activity summary:
        {json.dumps(recent_activity, indent=2)}

        Based on patterns in this user's Gmail, Search, and other activity,
        suggest new fields that should be added to PersonaOps tables
        to better capture emerging data needs.

        Return suggestions as JSON array:
        [
          {{
            "table": "table_name",
            "suggested_field": "field_name",
            "field_type": "TEXT/NUMBER/DATE/etc",
            "reasoning": "Explanation based on observed patterns",
            "evidence_source": "gmail/search/photos"
          }}
        ]
        """

        response = self.client.models.generate_content(
            model="gemini-3.1-pro-preview",
            contents=prompt,
            config=types.GenerateContentConfig(
                thinking_level="high",
                response_mime_type="application/json"
            )
        )

        return json.loads(response.text)

Example Enrichment Flow: \n

Voice Input: "Log a meeting with Sarah about the Q3 proposal"

Without Personal Intelligence:
→ Entities: { contact: "Sarah", topic: "Q3 proposal" }

With Personal Intelligence:
→ Gmail: Finds recent email from "Sarah Chen" with subject "Q3 Proposal Draft"
→ Calendar: Identifies meeting scheduled for tomorrow at 2 PM
→ Photos: (none relevant)
→ Search: Recent searches for "proposal templates"
→ YouTube: (none relevant)

Enriched Output:
{
  "intent": "CREATE_RECORD",
  "table": "Meeting_Notes",
  "entities": {
    "contact_name": "Sarah Chen",
    "contact_email": "[email protected]",
    "topic": "Q3 Proposal Review",
    "meeting_date": "2026-03-22T14:00:00Z",
    "related_document": "Q3 Proposal Draft (from Gmail attachment)",
    "preparation_notes": "Review proposal templates from recent searches"
  },
  "context_source": ["gmail", "calendar", "search"],
  "confidence": 0.94
}

Reference: TechCrunch coverage of Personal Intelligence beta announcement .


4.5 Tutorial 5: Document Generation from Voice-Derived Structured Data

Source: Adapted from Skywork.ai report assembly tutorial

Objective: Generate formatted Google Docs reports from PersonaOps sheet data using Gemini summarization.

Implementation: \n

// Apps Script - Generate report from PersonaOps data
function generateReportFromVoiceData(tableName, dateRange, templateId) {
  const registry = getSchemaRegistry();
  const tableInfo = registry.tables[tableName];

  if (!tableInfo) throw new Error(`Table ${tableName} not found`);

  // 1. Fetch data from PersonaOps sheet
  const sheet = SpreadsheetApp.openById(tableInfo.sheetId).getSheets()[0];
  const data = sheet.getDataRange().getValues();
  const headers = data[0];
  const rows = data.slice(1).filter(row => {
    // Filter by date range
    const dateCol = headers.indexOf('Date');
    if (dateCol === -1) return true;
    const rowDate = new Date(row[dateCol]);
    return rowDate >= dateRange.start && rowDate <= dateRange.end;
  });

  // 2. Generate AI summary of data using Gemini
  const summary = generateDataSummary(tableName, headers, rows);

  // 3. Create report from template
  const doc = createReportFromTemplate(templateId, {
    '{{table_name}}': tableName,
    '{{date_range}}': `${dateRange.start.toLocaleDateString()} - ${dateRange.end.toLocaleDateString()}`,
    '{{record_count}}': rows.length,
    '{{ai_summary}}': summary,
    '{{generated_date}}': new Date().toLocaleString()
  });

  // 4. Insert data table into document
  insertDataTable(doc.getId(), headers, rows);

  return {
    docUrl: doc.getUrl(),
    docId: doc.getId(),
    recordCount: rows.length
  };
}

/**
 * Generate AI summary of sheet data using Gemini
 */
function generateDataSummary(tableName, headers, rows) {
  // Prepare data sample for Gemini (limit token usage)
  const sampleSize = Math.min(rows.length, 50);
  const sample = rows.slice(0, sampleSize);

  // Convert to structured format
  const dataJson = sample.map(row => {
    const obj = {};
    headers.forEach((h, i) => obj[h] = row[i]);
    return obj;
  });

  const prompt = `
    Analyze this ${tableName} data from PersonaOps (${rows.length} total records, showing ${sampleSize} sample):
    ${JSON.stringify(dataJson, null, 2)}

    Provide:
    1. Executive summary (2-3 sentences)
    2. Key trends observed
    3. Notable outliers or anomalies
    4. Recommended actions

    Format as markdown.
  `;

  // Call Gemini via Apps Script
  const response = callGeminiAPI(prompt);
  return response;
}

/**
 * Call Gemini API from Apps Script
 */
function callGeminiAPI(prompt) {
  const apiKey = PropertiesService.getScriptProperties()
    .getProperty('GEMINI_API_KEY');

  const url = 'https://generativelanguage.googleapis.com/v1beta/models/gemini-3.1-pro-preview:generateContent';

  const response = UrlFetchApp.fetch(`${url}?key=${apiKey}`, {
    method: 'post',
    contentType: 'application/json',
    payload: JSON.stringify({
      contents: [{
        parts: [{ text: prompt }]
      }],
      generationConfig: {
        thinking_level: "high"
      }
    }),
    muteHttpExceptions: true
  });

  const data = JSON.parse(response.getContentText());
  return data.candidates[0].content.parts[0].text;
}

Reference: Skywork.ai report assembly patterns .


5. Application Ecosystem: Product Classification by Use Case

The PersonaOps-for-Google architecture enables a family of products differentiated by their primary Google API integration:

5.1 Field Operations & Mobile Capture

Primary APIs: Cloud Speech-to-Text (Chirp), Sheets API, Drive API

Use Case: Field workers capture observations, inspections, and transactions via voice on mobile devices.

Implementation Stack:

  • Voice Capture: Cloud Speech-to-Text with offline fallback
  • Schema Management: Sheets-based dynamic schemas
  • Offline Support: PWA with IndexedDB + background sync
  • Photo Attachments: Drive API for file upload with metadata

Key Differentiator: Works offline, syncs when connectivity restored.

5.2 Enterprise Knowledge Management

Primary APIs: Gemini Function Calling, Docs API, Personal Intelligence

Use Case: Meeting notes, decisions, and action items captured via voice and automatically structured into knowledge bases.

Implementation Stack:

  • Meeting Integration: Google Meet add-on for real-time transcription
  • Entity Extraction: Gemini with custom function declarations
  • Knowledge Store: Docs organized by project/topic with AI-generated summaries
  • Search: Personal Intelligence for cross-document retrieval

Key Differentiator: Personal Intelligence connects meeting content with email threads and documents automatically.

5.3 AI Agent Memory Systems

Primary APIs: Gemini API, Cloud Firestore, Vertex AI Vector Search

Use Case: Long-term memory for AI agents that persists across sessions.

Implementation Stack:

  • Memory Capture: Voice or text inputs → structured records
  • Vector Embeddings: Vertex AI text embeddings for semantic retrieval
  • Memory Store: Firestore with vector search capabilities
  • Context Window: Gemini 1M token context for session continuity

Key Differentiator: Combines structured schema (Sheets) with semantic search (vector embeddings).

5.4 Business Intelligence & Analytics

Primary APIs: BigQuery API, Looker Studio, Sheets API

Use Case: Voice-derived operational data automatically flows into analytics pipelines.

Implementation Stack:

  • Data Collection: Voice → Sheets (PersonaOps)
  • ETL Pipeline: Cloud Functions sync Sheets to BigQuery
  • Analytics: Scheduled queries in BigQuery
  • Visualization: Looker Studio dashboards with auto-refresh

Key Differentiator: Zero-ETL analytics from voice capture to dashboard.

5.5 Developer Workflow Automation

Primary APIs: Gemini Code Execution, Cloud Build, GitHub API

Use Case: Voice capture of bug reports, feature requests, and technical decisions → structured tickets and documentation.

Implementation Stack:

  • Voice Input: Google Cloud Speech-to-Text
  • Intent Routing: Gemini determines target system (GitHub Issues, Docs, Slack)
  • Action Execution: Function calling triggers appropriate API
  • Documentation: Auto-generated meeting notes with action items

Key Differentiator: Gemini's code execution capability enables voice-driven development workflows.


6. Technical Challenges & Google-Specific Mitigations

| Challenge | Google Ecosystem Mitigation | Reference | |----|----|----| | STT Latency | Chirp model with streaming recognition; partial results enable speculative processing | | | Entity Ambiguity | Gemini function calling with JSON schema enforcement; Personal Intelligence provides cross-app context | | | Schema Conflicts | Apps Script version control with non-breaking migration patterns; rollback via Properties Service | | | API Rate Limits | Exponential backoff with jitter (UrlFetchApp retry pattern); batch operations where possible | | | Offline Operation | PWA architecture with Workbox; Cloud Firestore offline persistence | Google Workbox docs | | Data Consistency | Eventual consistency with conflict resolution favoring human corrections | | | Security | IAM + OAuth2 scopes with least privilege; API keys stored in Secret Manager or Script Properties | |


7. Development Pathways: From MVP to Enterprise Scale

7.1 MVP Implementation (1-3 Engineer-Days)

Components:

  • Google Cloud Speech-to-Text (Chirp model)
  • Gemini API with function calling (gemini-3.1-pro-preview)
  • Google Sheets for schema registry and data store
  • Apps Script for automation triggers

Setup Steps:

  1. Enable required APIs in Google Cloud Console (Speech-to-Text, Sheets, Drive, Generative Language)
  2. Create OAuth 2.0 credentials with appropriate scopes
  3. Deploy Apps Script backend with schema management functions
  4. Configure Gemini function declarations for PersonaOps operations
  5. Build simple web interface for voice capture (or use Google Meet integration)

7.2 Production Architecture

┌─────────────────────────────────────────────────────────────────┐
│                    PRODUCTION PERSONAOPS                        │
│                     Google Ecosystem Stack                      │
└─────────────────────────────────────────────────────────────────┘

[Cloud Load Balancer]
         │
         ▼
[Cloud Run Services] ─────────────────────────────────────────────┐
├── Voice Gateway (WebRTC SFU)                                   │
├── STT Proxy (Speech-to-Text API)                               │
├── Gemini Orchestrator (Function Calling)                       │
└── Schema Service (Sheets API + Redis cache)                    │
         │                                                        │
         ▼                                                        │
[Cloud Workflows] ── Orchestration Layer ─────────────────────────┤
         │                                                        │
         ├──► [Sheets API] ── Structured Data Store              │
         ├──► [Docs API] ──── Unstructured Content               │
         ├──► [Drive API] ─── Attachments                        │
         ├──► [Gmail API] ─── Email Integration                  │
         └──► [BigQuery] ──── Analytics Sink                     │
                                                                  │
[Eventarc] ── Event Routing ─────────────────────────────────────┤
         │                                                        │
         └──► [Cloud Functions] ── Webhooks / External Sync      │
                                                                  │
[Personal Intelligence] ── Cross-App Context (Beta) ──────────────┘

7.3 Scaling Considerations

  • Concurrent Voice Streams: Cloud Run horizontal autoscaling based on concurrent connections
  • Gemini Rate Limits: Implement token bucket rate limiter; use batch processing for non-real-time classification
  • Sheets API Quotas: Cache schema registry in Redis/Memorystore; use batchUpdate for multi-row operations
  • Cost Optimization: Use Gemini Flash for simple classification; Gemini Pro for complex reasoning

8. Living Ecosystem: Compound Intelligence Across Google Services

The PersonaOps-for-Google architecture creates compounding intelligence effects as voice-derived data accumulates across Google services:

8.1 Compound Effects

| Data Accumulation | Resulting Intelligence | |----|----| | Voice → Sheets records accumulate | Gemini identifies patterns and suggests schema optimizations | | Sheets → BigQuery historical data | Looker Studio reveals trends that inform voice prompt tuning | | Docs meeting notes + Gmail threads | Personal Intelligence connects decisions to original context | | Photos visual data + Voice observations | Multimodal Gemini enriches text with visual verification | | Search history + Voice queries | Proactive schema suggestions based on emerging interests |

8.2 Self-Improving Loop

Voice Input ──► Structured Data ──► Analytics ──► Pattern Detection
     ▲                                              │
     │                                              ▼
     └─────────── Prompt Optimization ──┐   Schema Evolution
                                        │         │
                                        └────┬────┘
                                             ▼
                                    Improved Extraction Accuracy

Each cycle improves:

  1. Extraction Accuracy: Fine-tuned function calling based on correction history
  2. Schema Relevance: Proactive column additions based on emerging data patterns
  3. Context Enrichment: Personal Intelligence learns which cross-app sources provide value

8.3 Future Extensions

Multi-Modal Voice + Vision: \n Combine voice input with Google Lens / Camera for field data capture where visual context enriches spoken observations (e.g., "This equipment" + photo = specific asset ID).

Predictive Voice Prompts: \n Based on time, location (Google Maps), calendar (Google Calendar), and recent activity, Gemini proactively suggests data capture ("You're at the warehouse—would you like to log inventory?").

Autonomous Workflow Construction: \n Pattern detection across voice-derived data triggers automated workflow creation (e.g., "I've noticed you log purchase orders after every 'Low Stock' report. Would you like me to automate this?").


9. References

Official Google Documentation

  1. Gemini Function Calling - Google AI for Developers
  1. Gemini 3.1 Pro API Guide - Apidog Technical Guide
  1. Google Cloud Speech-to-Text - Vertex AI Documentation
  1. Build a Real-Time Voice Agent with Gemini & ADK - Google Cloud Blog
  1. Google AI & ML Architecture Center - Google Cloud

Third-Party Tutorials & Integration Guides

  1. Automate Email Triage, Sheets Updates & Report Assembly - Skywork.ai
  1. Automate Google Workspace Pipelines with Claude Haiku 4.5 - Skywork.ai

News & Announcements

  1. Gemini's Personal Intelligence Beta - TechCrunch
  1. Google Product Manager Persona Building - 數位時代

Additional Resources

  1. Google Generative AI Resources Collection - GitHub
  1. Helping Businesses with Generative AI - Cloud Ace

Appendix: API Reference Summary

| API/Service | Endpoint/Method | Primary Use in PersonaOps | |----|----|----| | Cloud Speech-to-Text | speech.googleapis.com/v1p1beta1/speech:streamingrecognize | Voice transcription with diarization | | Gemini API | generativelanguage.googleapis.com/v1beta/models/gemini-3.1-pro-preview:generateContent | Intent classification, entity extraction | | Sheets API | sheets.googleapis.com/v4/spreadsheets/{id}/values:append | Structured data persistence | | Sheets API | sheets.googleapis.com/v4/spreadsheets/{id}:batchUpdate | Schema modifications | | Docs API | docs.googleapis.com/v1/documents/{id}:batchUpdate | Unstructured content creation | | Drive API | www.googleapis.com/drive/v3/files | Template copying, file attachments | | Gmail API | gmail.googleapis.com/gmail/v1/users/{id}/messages | Email integration for data capture | | Apps Script | UrlFetchAppSpreadsheetAppDocumentApp | Automation and human-in-the-loop UI | | Personal Intelligence | Built into Gemini (no separate endpoint) | Cross-app context enrichment |


PersonaOps for Google Ecosystem — Version 1.0 — 2026

For Google Cloud Architects, Workspace Developers, and AI System Engineers.

This whitepaper provides a comprehensive technical foundation for implementing voice-to-data intelligence systems within the Google ecosystem, with all patterns grounded in official documentation and verified tutorials. The architecture is designed for immediate implementation while providing clear pathways to enterprise scale.

\

使用 Cassandra、GraphQL 和 AI 设计可扩展的产品目录 API

2026-04-25 22:31:49

This article breaks down how to design a retail system that actually works at scale using Cassandra, GraphQL, and AI. The key is not the tools themselves, but how you use them together: * Precompute as much as possible to avoid slow requests * Design for traffic spikes, not average load * Keep Cassandra queries predictable and optimized * Let AI handle decisions, and Cassandra handle fast retrieval Most performance issues come from mixing responsibilities or treating Cassandra like a relational database. Keeping roles clear and queries simple makes the system scalable and reliable.

关于自动密码套件排序的全部须知

2026-04-25 22:00:56

The Go standard library provides crypto/tls, a robust implementation of Transport Layer Security (TLS), the most important security protocol on the Internet, and the fundamental component of HTTPS. In Go 1.17 we made its configuration easier, more secure, and more efficient by automating the priority order of cipher suites.

How cipher suites work

Cipher suites date back to TLS’s predecessor Secure Socket Layer (SSL), which called them “cipher kinds”. They are the intimidating-looking identifiers like TLS_RSA_WITH_AES_256_CBC_SHA and TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 that spell out the algorithms used to exchange keys, authenticate certificates, and encrypt records in a TLS connection.

\ Cipher suites are negotiated during the TLS handshake: the client sends the list of cipher suites it supports in its first message, the Client Hello, and the server picks one from that list, communicating its choice to the client. The client sends the list of supported cipher suites in its own preference order, and the server is free to pick from it however it wants. Most commonly, the server will pick the first mutually supported cipher suite either in client preference order or in server preference order, based on its configuration.

\ Cipher suites are really only one of many negotiated parameters—supported curves/groups and signature algorithms are additionally negotiated through their own extensions—but are the most complex and famous ones, and the only ones that developers and administrators were trained over the years to have opinions on.

\ In TLS 1.0–1.2, all these parameters interact in a complex web of inter-dependencies: for example supported certificates depend on supported signature algorithms, supported curves, and supported cipher suites. In TLS 1.3 this was all drastically simplified: cipher suites only specify symmetric encryption algorithms, while supported curves/groups govern the key exchange and supported signature algorithms apply to the certificate.

A complex choice abdicated to developers

Most HTTPS and TLS servers delegate the choice of cipher suites and preference order to the server operator or the applications developer. This is a complex choice that requires up-to-date and specialized knowledge for many reasons.

\ Some older cipher suites have insecure components, some require extremely careful and complex implementations to be secure, and some are only secure if the client applies certain mitigations or even has certain hardware. Beyond the security of the individual components, different cipher suites can provide drastically different security properties for the whole connection, as cipher suites without ECDHE or DHE don’t provide forward secrecy—the property that connections can’t be retroactively or passively decrypted with the certificate’s key. Finally, the choice of supported cipher suites impacts compatibility and performance, and making changes without an up-to-date understanding of the ecosystem can lead to breaking connections from legacy clients, increasing the resources consumed by the server, or draining the batteries of mobile clients.

\ This choice is so arcane and delicate that there are dedicated tools to guide operators, such as the excellent Mozilla SSL Configuration Generator.

\ How did we get here and why is it like this?

\ To start, individual cryptographic components used to break much more often. In 2011, when the BEAST attack broke CBC cipher suites in such a way that only clients could mitigate the attack, servers moved to preferring RC4, which was unaffected. In 2013, when it became clear that RC4 was broken, servers went back to CBC. When Lucky Thirteen made it clear it was extremely hard to implement CBC cipher suites due to their backwards MAC-then-encrypt design… Well, there wasn’t anything else on the table so implementations had to carefully jump through hoops to implement CBC and kept failing at that daunting task for years. Configurable cipher suites and cryptographic agility used to provide some reassurance that when a component broke it could be replaced on the fly.

\ Modern cryptography is significantly different. Protocols can still break from time to time, but it’s rarely an individual abstracted component that fails. None of the AEAD-based cipher suites introduced starting with TLS 1.2 in 2008 have been broken. These days cryptographic agility is a liability: it introduces complexity that can lead to weaknesses or downgrades, and it is only necessary for performance and compliance reasons.

\ Patching used to be different, too. Today we acknowledge that promptly applying software patches for disclosed vulnerabilities is the cornerstone of secure software deployments, but ten years ago it was not standard practice. Changing configuration was seen as a much more rapid option to respond to vulnerable cipher suites, so the operator, through configuration, was put fully in charge of them. We now have the opposite problem: there are fully patched and updated servers that still behave weirdly, suboptimally, or insecurely, because their configurations haven’t been touched in years.

\ Finally, it was understood that servers tended to update more slowly than clients, and therefore were less reliable judges of the best choice of cipher suite. However, it’s servers who have the last word on cipher suite selection, so the default became to make servers defer to the client preference order, instead of having strong opinions. This is still partially true: browsers managed to make automatic updates happen and are much more up-to-date than the average server. On the other hand, a number of legacy devices are now out of support and are stuck on old TLS client configurations, which often makes an up-to-date server better equipped to choose than some of its clients.

\ Regardless of how we got here, it’s a failure of cryptography engineering to require application developers and server operators to become experts in the nuances of cipher suite selection, and to stay up-to-date on the latest developments to keep their configs up-to-date. If they are deploying our security patches, that should be enough.

\ The Mozilla SSL Configuration Generator is great, and it should not exist.

Is this getting any better?

\ There is good news and bad news for how things are trending in the past few years. The bad news is that ordering is getting even more nuanced, because there are sets of cipher suites that have equivalent security properties. The best choice within such a set depends on the available hardware and is hard to express in a config file. In other systems, what started as a simple list of cipher suites now depends on more complex syntax or additional flags like SSLOPPRIORITIZE_CHACHA.

\ The good news is that TLS 1.3 drastically simplified cipher suites, and it uses a disjoint set from TLS 1.0–1.2. All TLS 1.3 cipher suites are secure, so application developers and server operators shouldn’t have to worry about them at all. Indeed, some TLS libraries like BoringSSL and Go’s crypto/tls don’t allow configuring them at all.

Go’s crypto/tls and cipher suites

Go does allow configuring cipher suites in TLS 1.0–1.2. Applications have always been able to set the enabled cipher suites and preference order with Config.CipherSuites. Servers prioritize the client’s preference order by default, unless Config.PreferServerCipherSuites is set.

\ When we implemented TLS 1.3 in Go 1.12, we didn’t make TLS 1.3 cipher suites configurable, because they are a disjoint set from the TLS 1.0–1.2 ones and most importantly they are all secure, so there is no need to delegate a choice to the application. Config.PreferServerCipherSuites still controls which side’s preference order is used, and the local side’s preferences depend on the available hardware.

\ In Go 1.14 we exposed supported cipher suites, but explicitly chose to return them in a neutral order (sorted by their ID), so that we wouldn’t end up tied to representing our priority logic in terms of a static sort order.

\ In Go 1.16, we started actively preferring ChaCha20Poly1305 cipher suites over AES-GCM on the server when we detect that either the client or the server lacks hardware support for AES-GCM. This is because AES-GCM is hard to implement efficiently and securely without dedicated hardware support (such as the AES-NI and CLMUL instruction sets).

\ Go 1.17, recently released, takes over cipher suite preference ordering for all Go users. While Config.CipherSuites still controls which TLS 1.0–1.2 cipher suites are enabled, it is not used for ordering, and Config.PreferServerCipherSuites is now ignored. Instead, crypto/tls makes all ordering decisions, based on the available cipher suites, the local hardware, and the inferred remote hardware capabilities.

\ The current TLS 1.0–1.2 ordering logic follows the following rules:

  1. ECDHE is preferred over the static RSA key exchange.

    The most important property of a cipher suite is enabling forward secrecy. We don’t implement “classic” finite-field Diffie-Hellman, because it’s complex, slower, weaker, and subtly broken in TLS 1.0–1.2, so that means prioritizing the Elliptic Curve Diffie-Hellman key exchange over the legacy static RSA key exchange. (The latter simply encrypts the connection’s secret using the certificate’s public key, making it possible to decrypt if the certificate is compromised in the future.)

  2. AEAD modes are preferred over CBC for encryption.

    Even if we do implement partial countermeasures for Lucky13 (my first contribution to the Go standard library, back in 2015!), the CBC suites are a nightmare to get right, so all other more important things being equal, we pick AES-GCM and ChaCha20Poly1305 instead.

  3. 3DES, CBC-SHA256, and RC4 are only used if nothing else is available, in that preference order.

    3DES has 64-bit blocks, which makes it fundamentally vulnerable to birthday attacks given enough traffic. 3DES is listed under InsecureCipherSuites, but it’s enabled by default for compatibility. (An additional benefit of controlling preference orders is that we can afford to keep less secure cipher suites enabled by default without worrying about applications or clients selecting them except as a last resort. This is safe because there are no downgrade attacks that rely on the availability of a weaker cipher suite to attack peers that support better alternatives.)

    The CBC cipher suites are vulnerable to Lucky13-style side channel attacks and we only partially implement the complex countermeasures discussed above for the SHA-1 hash, not for SHA-256. CBC-SHA1 suites have compatibility value, justifying the extra complexity, while the CBC-SHA256 ones don’t, so they are disabled by default.

    RC4 has practically exploitable biases that can lead to plaintext recovery without side channels. It doesn’t get any worse than this, so RC4 is disabled by default.

  4. ChaCha20Poly1305 is preferred over AES-GCM for encryption, unless both sides have hardware support for the latter.

    As we discussed above, AES-GCM is hard to implement efficiently and securely without hardware support. If we detect that there isn’t local hardware support or (on the server) that the client has not prioritized AES-GCM, we pick ChaCha20Poly1305 instead.

  5. AES-128 is preferred over AES-256 for encryption.

    AES-256 has a larger key than AES-128, which is usually good, but it also performs more rounds of the core encryption function, making it slower. (The extra rounds in AES-256 are independent of the key size change; they are an attempt to provide a wider margin against cryptanalysis.) The larger key is only useful in multi-user and post-quantum settings, which are not relevant to TLS, which generates sufficiently random IVs and has no post-quantum key exchange support. Since the larger key doesn’t have any benefit, we prefer AES-128 for its speed.

\ TLS 1.3’s ordering logic needs only the last two rules, because TLS 1.3 eliminated the problematic algorithms the first three rules are guarding against.

FAQs

What if a cipher suite turns out to be broken? Just like any other vulnerability, it will be fixed in a security release for all supported Go versions. All applications need to be prepared to apply security fixes to operate securely. Historically, broken cipher suites are increasingly rare.

\ Why leave enabled TLS 1.0–1.2 cipher suites configurable? There is a meaningful tradeoff between baseline security and legacy compatibility to make in choosing which cipher suites to enable, and that’s a choice we can’t make ourselves without either cutting out an unacceptable slice of the ecosystem, or reducing the security guarantees of modern users.

\ Why not make TLS 1.3 cipher suites configurable? Conversely, there is no tradeoff to make with TLS 1.3, as all its cipher suites provide strong security. This lets us leave them all enabled and pick the fastest based on the specifics of the connection without requiring the developer’s involvement.

Key takeaways

Starting in Go 1.17, crypto/tls is taking over the order in which available cipher suites are selected. With a regularly updated Go version, this is safer than letting potentially outdated clients pick the order, lets us optimize performance, and it lifts significant complexity from Go developers.

\ This is consistent with our general philosophy of making cryptographic decisions whenever we can, instead of delegating them to developers, and with our cryptography principles. Hopefully other TLS libraries will adopt similar changes, making delicate cipher suite configuration a thing of the past.


Filippo Valsorda

\ This article is available on The Go Blog under a CC BY 4.0 DEED license.

\ Photo by Brett Jordan on Unsplash

\

人工智能真的是新一代编译器吗?

2026-04-25 22:00:53

\ Amid the ongoing discussion surrounding whether technical knowledge is still needed in the age of AI, I noticed one particularly popular argument:

"With AI, no one will have to look at code anymore, just like we no longer have to look at the machine code or bytecode produced by compilers."

\ The comparison seems sound at first glance. We began programming with electric circuits and punched cards, moved to assembly code, then to low-level languages like C and C++, and eventually arrived at high-level languages like Java, Python, or JavaScript. With each evolution, we move one step closer to describing our intent in natural language. Now, with AI, that journey seems to have finally reached its destination.

\ Compilers and interpreters translate high-level programming languages into machine code or bytecode that we rarely need to read. Similarly, many assume that because AI translates natural language into a programming language, we can finally stop caring about the underlying code altogether.

\ I used to find this argument persuasive, but after thinking deeper, I realized this school of thought misses two critical points.

1. Compilers are deterministic, while AI is probabilistic.

When using a compiler, you can be confident that the low-level output is 100% faithful to the high-level source code. Any bugs in your program stem from how you translated your thoughts into the high-level language, not from the compilation process itself. This is why you do not need to inspect the output.

\ In contrast, AI is a sophisticated statistical machine that cannot guarantee a consistent, identical output for every prompt. Because it is probabilistic, ==you can never be entirely confident in AI-generated code without verification.==

\ One might argue, "That is just the current state of AI. You don't know how it might evolve in a few years." Even if AI technology advances to eliminate its probabilistic nature and becomes deterministic, would that remove the need to verify the code? I believe the answer is still no. The issue is not just the technology; it is also the way natural languages are constructed.

2. Programming languages are unambiguous, while natural languages are full of holes.

You may hate learning programming-language syntax or feel frustrated when you encounter syntax errors, but in programming, these are features, not bugs.

\ Programming languages force us to carefully think, break down requirements, and describe them with a level of precision that cannot be mistaken for anything else. There is no room for guessing, misunderstanding, or vague assumptions. You must be 100% clear about your intent. Once you write valid instructions, you can trust the machine to follow them perfectly.

\ Human language, however, is so flexible and lenient. A single sentence can be interpreted in several different ways. When we communicate, much more frequently than we realize, we unconsciously make assumptions and fill in gaps based on our own subjective knowledge and experience.

\ ==By describing your intent in natural language, you leave significant room for interpretation== and lose total control over the outcome. AI must take on the job of filling in those holes, guessing the missing pieces, and making decisions for you: a task with no definitive correct answers. Whether the translation process is probabilistic or deterministic, ignoring the output code poses a real risk that the software will not behave as intended.

"I can just write tests to ensure my AI-generated code behaves as intended."

This is a naive perspective. While this might work for a simple proof-of-concept, tests are not a sufficient replacement for understanding a system in production-grade software.

\ ==Tests are meant to provide a layer of assurance on top of a well-designed, well-understood codebase, not to replace it.== Your test suite should be a safety net that protects critical paths and catches edge cases, not an exhaustive description of every possible behavior. If you attempt to use tests as a cover-up for not understanding the code, the cost of producing and maintaining that test suite will quickly exceed the cost of simply writing the code from scratch.

"Same same, but different."

AI is certainly acting as a next-generation compiler by translating requirements from natural language into code. It allows us to focus more on high-level ideas and less on minor implementation details.

\ However, unlike traditional compilers, AI is not completely trustworthy, and describing what you want in natural language can never guarantee the outcome is exactly what you desire. Therefore, it would be a serious mistake to think you can treat your AI-generated code the same way you treat a compiler's output.

\ \ I believe care should only end where responsibility ends. Only you, not AI, bear responsibility for the final product, and caring about the code is one of the best ways you can practice that responsibility.

\