|
101 | 101 | "\n",
|
102 | 102 | "## 1\\. Scenario Snapshot\n",
|
103 | 103 | "\n",
|
104 |
| - "* **Corpus:** The primary document is the [Trademark Trial and Appeal Board Manual of Procedure (TBMP, 2019 version)](https://www.uspto.gov/sites/default/files/documents/tbmp-2019.pdf). This manual contains detailed procedural rules and guidelines, coming to 1194 pages total. \n", |
| 104 | + "* **Corpus:** The primary document is the [Trademark Trial and Appeal Board Manual of Procedure (TBMP, 2024 version)](https://www.uspto.gov/sites/default/files/documents/tbmp-Master-June2024.pdf). This manual contains detailed procedural rules and guidelines, coming to 1194 pages total. \n", |
105 | 105 | "* **Users:** The target users are intellectual property (IP) litigation associates and paralegals who need quick, accurate answers to procedural questions based *only* on the TBMP. \n",
|
106 | 106 | "* **Typical Asks:** Users pose questions requiring synthesis and citation, such as: \n",
|
107 | 107 | " 1. \"What are the requirements for filing a motion to compel discovery according to the TBMP?\" \n",
|
|
2440 | 2440 | "\n",
|
2441 | 2441 | "\n",
|
2442 | 2442 | "\n",
|
2443 |
| - "Many businesses are faced with the task of digitizing hand filled forms. In this section, we will demonstrate how OpenAI can be used to digitize and validate a hand filled insurance form. While this is a common problem for insurance, the same techniques can be applied to a variety of other industries and forms, for example tax forms, invoices, and more.\n", |
| 2443 | + "Many businesses are faced with the task of digitizing hand-filled forms. In this section, we will demonstrate how OpenAI can be used to digitize and validate a hand-filled insurance form. While this is a common problem for insurance, the same techniques can be applied to a variety of other industries and forms, for example tax forms, invoices, and more.\n", |
2444 | 2444 | "\n",
|
2445 | 2445 | "## 🗂️ TL;DR Matrix\n",
|
2446 | 2446 | "\n",
|
|
2489 | 2489 | },
|
2490 | 2490 | {
|
2491 | 2491 | "cell_type": "code",
|
2492 |
| - "execution_count": 20, |
| 2492 | + "execution_count": 6, |
2493 | 2493 | "id": "923344db",
|
2494 | 2494 | "metadata": {},
|
2495 | 2495 | "outputs": [
|
|
2519 | 2519 | },
|
2520 | 2520 | {
|
2521 | 2521 | "cell_type": "code",
|
2522 |
| - "execution_count": 21, |
| 2522 | + "execution_count": 7, |
2523 | 2523 | "id": "7ccd93f6",
|
2524 | 2524 | "metadata": {},
|
2525 | 2525 | "outputs": [],
|
|
2592 | 2592 | "source": [
|
2593 | 2593 | "**Flow Explanation: Stage 1**\n",
|
2594 | 2594 | "\n",
|
2595 |
| - "1. **Image:** The image of the form taken from the user's smartphone is passed to the model. OpenAI's models can accept a variety of image formats, but we typically use a PNG format to keep the text crisp and reduce artifacts. For this example, we pass the image to the model from a publically available content URL. In a production environment, you likely would pass the image as a signed URL to an image hosted in your own cloud storage bucket. \n", |
| 2595 | + "1. **Image:** The image of the form taken from the user's smartphone is passed to the model. OpenAI's models can accept a variety of image formats, but we typically use a PNG format to keep the text crisp and reduce artifacts. For this example, we pass the image to the model from a publicly available content URL. In a production environment, you likely would pass the image as a signed URL to an image hosted in your own cloud storage bucket. \n", |
2596 | 2596 | " \n",
|
2597 | 2597 | "2. **Structured Output Schema:** We define a Pydantic model that sets the structure of the output data. The model includes all of the fields that we need to extract from the form, along with the appropriate types for each field. Our model is broken into several subcomponents, each of which is a Pydantic model itself and referenced by the parent model."
|
2598 | 2598 | ]
|
2599 | 2599 | },
|
2600 | 2600 | {
|
2601 | 2601 | "cell_type": "code",
|
2602 |
| - "execution_count": 22, |
| 2602 | + "execution_count": 8, |
2603 | 2603 | "id": "59263ec9",
|
2604 | 2604 | "metadata": {},
|
2605 | 2605 | "outputs": [],
|
|
2620 | 2620 | "\n",
|
2621 | 2621 | "class DwellingDetails(BaseModel):\n",
|
2622 | 2622 | " coverage_a_limit: str\n",
|
2623 |
| - " compantion_policy_expiration_date: str\n", |
| 2623 | + " companion_policy_expiration_date: str\n", |
2624 | 2624 | " occupancy_of_dwelling: str\n",
|
2625 | 2625 | " type_of_policy: str\n",
|
2626 | 2626 | " unrepaired_structural_damage: bool\n",
|
|
2654 | 2654 | "id": "70e746a3",
|
2655 | 2655 | "metadata": {},
|
2656 | 2656 | "source": [
|
2657 |
| - "3. **Run OCR:** Using the vision capabilities of GPT-4.1, we run the first stage of our pipeline to extract the text from the document in a structured format. This initial stage aims to achieve high accuracy while passing through uncertainty to the second stage. Our prompt explicitly instructs the model to avoid inferring inputs and instead to fill out the details as exact as possible. For the image input, we set image input detail to `auto` to infer a detail level that's appropriate to the image. We found in our experiments that `auto` worked well, but if you are seeing quality issues in your OCR processing considering using `high`." |
| 2657 | + "3. **Run OCR:** Using the vision capabilities of GPT-4.1, we run the first stage of our pipeline to extract the text from the document in a structured format. This initial stage aims to achieve high accuracy while passing through uncertainty to the second stage. Our prompt explicitly instructs the model to avoid inferring inputs and instead to fill out the details as exact as possible. For the image input, we set image input detail to `auto` to infer a detail level that's appropriate to the image. We found in our experiments that `auto` worked well, but if you are seeing quality issues in your OCR processing consider using `high`." |
2658 | 2658 | ]
|
2659 | 2659 | },
|
2660 | 2660 | {
|
2661 | 2661 | "cell_type": "code",
|
2662 |
| - "execution_count": 23, |
| 2662 | + "execution_count": 9, |
2663 | 2663 | "id": "1537dad2",
|
2664 | 2664 | "metadata": {},
|
2665 | 2665 | "outputs": [
|
2666 |
| - { |
2667 |
| - "name": "stderr", |
2668 |
| - "output_type": "stream", |
2669 |
| - "text": [ |
2670 |
| - "HTTP Request: POST https://api.openai.com/v1/responses \"HTTP/1.1 200 OK\"\n" |
2671 |
| - ] |
2672 |
| - }, |
2673 | 2666 | {
|
2674 | 2667 | "name": "stdout",
|
2675 | 2668 | "output_type": "stream",
|
|
2707 | 2700 | " \"companion_policy_number\": \"81265919\",\n",
|
2708 | 2701 | " \"dwelling_details\": {\n",
|
2709 | 2702 | " \"coverage_a_limit\": \"$900,000\",\n",
|
2710 |
| - " \"compantion_policy_expiration_date\": \"5/31/27\",\n", |
| 2703 | + " \"companion_policy_expiration_date\": \"5/31/27\",\n", |
2711 | 2704 | " \"occupancy_of_dwelling\": \"Owner\",\n",
|
2712 | 2705 | " \"type_of_policy\": \"Homeowners\",\n",
|
2713 | 2706 | " \"unrepaired_structural_damage\": false,\n",
|
|
2734 | 2727 | "OCR_PROMPT = \"\"\"You are a helpful assistant who excels at processing insurance forms.\n",
|
2735 | 2728 | "\n",
|
2736 | 2729 | "You will be given an image of a hand-filled insurance form. Your job is to OCR the data into the given structured format.\n",
|
2737 |
| - "Fill out the fields as exactly as possible. If a written character could possibly be ambigious (i.e. l or 1, o or 0), include all possiblities in the field separated by \"OR\", especially for email addresses.\n", |
| 2730 | + "Fill out the fields as exactly as possible. If a written character could possibly be ambiguous (i.e. l or 1, o or 0), include all possiblities in the field separated by \"OR\", especially for email addresses.\n", |
2738 | 2731 | "\"\"\"\n",
|
2739 | 2732 | "\n",
|
2740 | 2733 | "user_content = [\n",
|
|
2777 | 2770 | },
|
2778 | 2771 | {
|
2779 | 2772 | "cell_type": "code",
|
2780 |
| - "execution_count": 24, |
| 2773 | + "execution_count": 10, |
2781 | 2774 | "id": "72dc150e",
|
2782 | 2775 | "metadata": {},
|
2783 | 2776 | "outputs": [],
|
|
2830 | 2823 | },
|
2831 | 2824 | {
|
2832 | 2825 | "cell_type": "code",
|
2833 |
| - "execution_count": 25, |
| 2826 | + "execution_count": 11, |
2834 | 2827 | "id": "ae8fcf6d",
|
2835 | 2828 | "metadata": {},
|
2836 | 2829 | "outputs": [],
|
2837 | 2830 | "source": [
|
2838 | 2831 | "PROMPT = \"\"\"You are a helpful assistant who excels at processing insurance forms.\n",
|
2839 | 2832 | "\n",
|
2840 |
| - "You will be given a javascript representation of an OCR'd document. Consider at which fields are ambigious reason about how to fill them in. Fill any missing fields that are possible to infer from existing data, or search the web. If you cannot fill a field, reason about why.\n", |
| 2833 | + "You will be given a javascript representation of an OCR'd document. Consider at which fields are ambiguous reason about how to fill them in. Fill any missing fields that are possible to infer from existing data, or search the web. If you cannot fill a field, reason about why.\n", |
2841 | 2834 | "\n",
|
2842 | 2835 | "Use the tools provided if necessary to clarify the results. If the OCR system has provided two possibilities, do your best to definitely pick which option is correct.\n",
|
2843 | 2836 | "\"\"\""
|
2844 | 2837 | ]
|
2845 | 2838 | },
|
2846 | 2839 | {
|
2847 | 2840 | "cell_type": "code",
|
2848 |
| - "execution_count": 26, |
| 2841 | + "execution_count": 12, |
2849 | 2842 | "id": "1d2b77ee",
|
2850 | 2843 | "metadata": {},
|
2851 | 2844 | "outputs": [
|
2852 | 2845 | {
|
2853 | 2846 | "name": "stdout",
|
2854 | 2847 | "output_type": "stream",
|
2855 | 2848 | "text": [
|
2856 |
| - "Requesting completion from model 'o4-mini-2025-04-16' (messages=2)\n" |
2857 |
| - ] |
2858 |
| - }, |
2859 |
| - { |
2860 |
| - "name": "stderr", |
2861 |
| - "output_type": "stream", |
2862 |
| - "text": [ |
2863 |
| - "HTTP Request: POST https://api.openai.com/v1/responses \"HTTP/1.1 200 OK\"\n" |
2864 |
| - ] |
2865 |
| - }, |
2866 |
| - { |
2867 |
| - "name": "stdout", |
2868 |
| - "output_type": "stream", |
2869 |
| - "text": [ |
2870 |
| - "Assistant requested tool calls, resolving ...\n", |
2871 |
| - "Tool call search_web complete, result: 855 Brannan St, San Francisco, 94103, San Francisco County\n", |
2872 |
| - "Requesting completion from model 'o4-mini-2025-04-16' (messages=5)\n" |
2873 |
| - ] |
2874 |
| - }, |
2875 |
| - { |
2876 |
| - "name": "stderr", |
2877 |
| - "output_type": "stream", |
2878 |
| - "text": [ |
2879 |
| - "HTTP Request: POST https://api.openai.com/v1/responses \"HTTP/1.1 200 OK\"\n" |
2880 |
| - ] |
2881 |
| - }, |
2882 |
| - { |
2883 |
| - "name": "stdout", |
2884 |
| - "output_type": "stream", |
2885 |
| - "text": [ |
| 2849 | + "Requesting completion from model 'o4-mini-2025-04-16' (messages=2)\n", |
2886 | 2850 | "Assistant requested tool calls, resolving ...\n",
|
2887 | 2851 | "Tool call validate_email complete, result: True\n",
|
2888 |
| - "Requesting completion from model 'o4-mini-2025-04-16' (messages=8)\n" |
2889 |
| - ] |
2890 |
| - }, |
2891 |
| - { |
2892 |
| - "name": "stderr", |
2893 |
| - "output_type": "stream", |
2894 |
| - "text": [ |
2895 |
| - "HTTP Request: POST https://api.openai.com/v1/responses \"HTTP/1.1 200 OK\"\n" |
2896 |
| - ] |
2897 |
| - }, |
2898 |
| - { |
2899 |
| - "name": "stdout", |
2900 |
| - "output_type": "stream", |
2901 |
| - "text": [ |
| 2852 | + "Requesting completion from model 'o4-mini-2025-04-16' (messages=5)\n", |
2902 | 2853 | "Assistant requested tool calls, resolving ...\n",
|
2903 | 2854 | "Tool call validate_email complete, result: False\n",
|
2904 |
| - "Requesting completion from model 'o4-mini-2025-04-16' (messages=11)\n" |
2905 |
| - ] |
2906 |
| - }, |
2907 |
| - { |
2908 |
| - "name": "stderr", |
2909 |
| - "output_type": "stream", |
2910 |
| - "text": [ |
2911 |
| - "HTTP Request: POST https://api.openai.com/v1/responses \"HTTP/1.1 200 OK\"\n" |
2912 |
| - ] |
2913 |
| - }, |
2914 |
| - { |
2915 |
| - "name": "stdout", |
2916 |
| - "output_type": "stream", |
2917 |
| - "text": [ |
| 2855 | + "Requesting completion from model 'o4-mini-2025-04-16' (messages=8)\n", |
2918 | 2856 | "Received parsed result from model\n",
|
2919 | 2857 | "{\n",
|
2920 | 2858 | " \"applicant\": {\n",
|
|
2935 | 2873 | " \"street\": \"855 Brannan St\",\n",
|
2936 | 2874 | " \"city\": \"San Francisco\",\n",
|
2937 | 2875 | " \"state\": \"CA\",\n",
|
2938 |
| - " \"zip\": \"94103\",\n", |
| 2876 | + " \"zip\": \"94107\",\n", |
2939 | 2877 | " \"county\": \"San Francisco\"\n",
|
2940 | 2878 | " },\n",
|
2941 | 2879 | " \"mailing_address_if_different_than_risk_address\": {\n",
|
2942 |
| - " \"street\": \"\",\n", |
2943 |
| - " \"city\": \"\",\n", |
2944 |
| - " \"state\": \"\",\n", |
2945 |
| - " \"zip\": \"\",\n", |
2946 |
| - " \"county\": \"\"\n", |
| 2880 | + " \"street\": \"855 Brannan St\",\n", |
| 2881 | + " \"city\": \"San Francisco\",\n", |
| 2882 | + " \"state\": \"CA\",\n", |
| 2883 | + " \"zip\": \"94107\",\n", |
| 2884 | + " \"county\": \"San Francisco\"\n", |
2947 | 2885 | " },\n",
|
2948 | 2886 | " \"participating_insurer\": \"Acme Insurance Co\",\n",
|
2949 | 2887 | " \"companion_policy_number\": \"81265919\",\n",
|
2950 | 2888 | " \"dwelling_details\": {\n",
|
2951 | 2889 | " \"coverage_a_limit\": \"$900,000\",\n",
|
2952 |
| - " \"compantion_policy_expiration_date\": \"5/31/27\",\n", |
| 2890 | + " \"companion_policy_expiration_date\": \"5/31/27\",\n", |
2953 | 2891 | " \"occupancy_of_dwelling\": \"Owner\",\n",
|
2954 | 2892 | " \"type_of_policy\": \"Homeowners\",\n",
|
2955 | 2893 | " \"unrepaired_structural_damage\": false,\n",
|
|
3015 | 2953 | "\n",
|
3016 | 2954 | "To help us understand and debug the model, we can also print the summary chain-of-thought reasoning produced by the model. This can help expose common failure modes, points where the model is unclear, or incorrect upstream details.\n",
|
3017 | 2955 | "\n",
|
3018 |
| - "While developing this solution the chain-of-thought summaries exposed some incorrectly named and typed schema values." |
| 2956 | + "While developing this solution, the chain-of-thought summaries exposed some incorrectly named and typed schema values." |
3019 | 2957 | ]
|
3020 | 2958 | },
|
3021 | 2959 | {
|
3022 | 2960 | "cell_type": "code",
|
3023 |
| - "execution_count": 27, |
| 2961 | + "execution_count": 13, |
3024 | 2962 | "id": "ab1d4fbc",
|
3025 | 2963 | "metadata": {},
|
3026 | 2964 | "outputs": [
|
3027 | 2965 | {
|
3028 | 2966 | "name": "stdout",
|
3029 | 2967 | "output_type": "stream",
|
3030 | 2968 | "text": [
|
3031 |
| - "**Filling in missing fields**\n", |
| 2969 | + "**Determining insurance form details**\n", |
| 2970 | + "\n", |
| 2971 | + "I have a JSON representation of a partially filled insurance form, and there are a few missing or ambiguous fields that I need to address.\n", |
3032 | 2972 | "\n",
|
3033 |
| - "The user has given me an OCR result map, and I need to address the missing fields. For the applicant's email, we have two options: \"[email protected]\" or \"[email protected].\" I should validate which one is correct. For the address, 855 Brannan St, SF, it's likely in the 94107 zip code rather than 94103, given its location. San Francisco County is the correct county. For the mailing address, I could either note it as \"Same as risk address\" or leave it empty if they're the same.\n", |
| 2973 | + "For the email address, I see two options. I can validate which one is correct by checking both with the tool.\n", |
3034 | 2974 | "\n",
|
3035 |
| - "**Considering mailing address input**\n", |
| 2975 | + "The risk address fields for zip code and county are empty. Based on the address \"855 Brannan St, San Francisco, CA,\" I can determine the correct zip code is 94107, as that area corresponds to South Beach. Lastly, since the mailing address is empty, I assume it's the same as the risk address.\n", |
3036 | 2976 | "\n",
|
3037 |
| - "I'm looking at the applicant's email and the mailing address input. The field for \"mailing_address_if_different_than_risk_address\" can be tricky. If the mailing address is the same as the risk address, I think it’s best to leave it blank. The schema requires empty strings for optional fields, so filling it with \"same\" might lead to confusion about whether the addresses differ. I’ll confirm that it should ideally just remain empty to avoid any misleading implications.\n", |
| 2977 | + "**Filling insurance form details**\n", |
3038 | 2978 | "\n",
|
3039 |
| - "**Clarifying mailing address input**\n", |
| 2979 | + "I think it’s best to set the mailing address to be the same as the risk address or clarify that a blank one implies the same. Since it’s an explicit instruction to fill missing fields, I’ll fill in the mailing address with the risk address to avoid confusion.\n", |
3040 | 2980 | "\n",
|
3041 |
| - "I’m looking at the \"mailing_address_if_different_than_risk_address.\" If the addresses are the same, I think it’s best to leave those fields empty since they're only necessary if they differ. The instruction is to fill missing fields that can be inferred, but this mailing address isn’t technically missing. I should focus on filling in the zip and county for the risk address. So, the final output will include these details with other fields remaining as they are.\n", |
| 2981 | + "All co-applicant fields are present, and dwelling details are complete. The effective and expiration dates are also provided. I plan to validate both email options by checking each one separately. Let's begin with validating the first email.\n", |
3042 | 2982 | "\n"
|
3043 | 2983 | ]
|
3044 | 2984 | }
|
|
3091 | 3031 | "| :---- | :---- | :---- | :---- |\n",
|
3092 | 3032 | "| Input | 2,000 | $1.00 | $0.002 |\n",
|
3093 | 3033 | "| Output | 1,500 | $4.00 | $0.006 |\n",
|
3094 |
| - "| **Total (Stage 1\\)** | | | **$8.00** |\n", |
| 3034 | + "| **Total for 1,000 pages (Stage 1\\)** | | | **$8.00** |\n", |
3095 | 3035 | "\n",
|
3096 | 3036 | "#### **Stage 2: Reasoning**\n",
|
3097 | 3037 | "\n",
|
|
3101 | 3041 | "| :---- | :---- | :---- | :---- |\n",
|
3102 | 3042 | "| Input | 2,000 | $0.55 | $0.0011 |\n",
|
3103 | 3043 | "| Output | 3,000 | $2.20 | $0.0066 |\n",
|
3104 |
| - "| **Total (Stage 2\\)** | | | **$7.70** |\n", |
| 3044 | + "| **Total for 1,000 pages (Stage 2\\)** | | | **$7.70** |\n", |
3105 | 3045 | "\n",
|
3106 | 3046 | "#### Grand Total (per 1,000 pages): **$15.70**\n",
|
3107 | 3047 | "\n",
|
|
0 commit comments